Live-session port (2026-10-02→04): the packed courier codec (C19 closed — ALPN apns-pack-v1:4096:4096 negotiated live, packed chain anchors at the Apple Server Authentication CA intermediate, no TLS client cert on that path), the <Protocol>-span extraction for wrapped activation responses, the accepted Albert shapes (DeviceCertRequest as <data>(base64(PEM)), three-cert FairPlayCertChain), kebab-case sign-in response + client-id parsing, fresh anisette per SRP run and per 2FA call (the 434/-22421 fixes), the GSA postdata liveness client (fired AFTER the delegate sign-in, ptkn + hb-token binding), and the validation-stamp replay guard (one mint = one device record — the 409's true root cause); nullable next-hbi was already end-to-end

This commit is contained in:
fenleon committed 2026-10-05 00:22:45 +02:00
1 parent 2efd4d0f19
commit 7f960dbdfc
25 files changed
+1138 -84

No files matched your search

+11 -9
View File
@@ -1,6 +1,6 @@
# iMessage protocol spec (clean-room)
**Status:** draft — revision 25. The full revision history is at the bottom ([Revision history](#revision-history)); the sections below state the facts as of rev 25.
**Status:** draft — revision 26. The full revision history is at the bottom ([Revision history](#revision-history)); the sections below state the facts as of rev 26.
**Firewall rule:** this document contains facts only — no code, no function/type names, no copied expression from any reference implementation. Wire-format field names are quoted as on-the-wire protocol data (observable in captures, capture-arbitrated), never as source expression. Facts derived from SSPL/AGPL sources are used facts-only; nothing from them is reproduced. Where the source-derived fact sets agree, the fact is stated once with both citations; where they disagree, the disagreement is stated and tagged `TODO(capture):` — running mitmproxy captures arbitrate. All `TODO(capture)` items are collected in §7.
@@ -20,7 +20,7 @@
- For the real-Mac path, validation data is produced by a three-step exchange with Apple: download a certificate from the `id-validation-cert` bag URL; POST a session-info request to `id-initialize-validation`; then run key establishment with the returned session info and produce a signed blob [RP:macos.rs]. Extraction detail (2026-09-10): step 1's response is a plist with a single `cert` data field; step 2 POSTs a single-key body `session-info-request` (data) with no anisette or authentication headers — body only — and receives `session-info` (data) back [RP:macos.rs], [RP:auth.rs]. — 2026-09-11 (rev 19): the step-2 request construction is recorded at the same level of detail — the body is serialized as an XML plist and posted with NO content-type header, from an HTTP client whose only default request header is `Accept-Language: en-US,en;q=0.9` [RP:util.rs], [RP:macos.rs]. A header-less step-1 GET succeeds because the `id-validation-cert` URL resolves to a CDN static object (a genuine device's capture of that same GET shows `static.ess.apple.com`, plain HTTP, `X-Cache: hit-fresh`, a 2021 `Last-Modified`, `application/octet-stream`, 3474 bytes), so step 1's header-less success is NOT evidence that the endpoints accept header-less requests (§7 item 24). The hardware configuration the context consumes carries product name, OS build number, platform serial number, platform UUID, root-disk UUID, board ID, MAC address (6 bytes), ROM (bytes), and MLB (string), plus pre-computed obfuscated "enc" variants of serial/platform-UUID/root-disk-UUID/ROM/MLB keyed by five fixed label strings (`Gq3489ugfi`, `Fyp98tpgj`, `kbjfrfpoJU`, `oycqAZloTNDm`, `abKPld1EcMni`); the derivation producing the enc variants is closed source — `TODO(capture):` → C57. The blob is parseable back into the hardware fields (round-trip constructor in the mock's API surface) [OA], [RP:macos.rs].
- Current shipped flow (OpenBubbles, 2026): the Mac-side helper (`Mac-Hardware-Info` 1.2.0, notarized) only *reads* the machine identifiers — serial, board ID, model, etc. — via public IORegistry/sysctl APIs (docs: "High Sierra and above", no upper macOS version bound) and exports them as a shareable code (≤ 20 users per code, optional one-time-use/48-hour mode); it never invokes an Apple private framework [OB-MHI], [OB-FAQ]. The client then performs the three-step exchange above itself, in software, keyed with those identifiers and paired with an Apple-Account login — the [JJ] emulation path with an official identifier-export helper in front of it. `TODO(capture):` **C56** — the client-side generation procedure behind the three-step exchange: what the client computes with the `id-initialize-validation` session info (the key-establishment math and blob assembly), how it interacts with the Apple-Account/GSA login and the anisette header set (C6) at activation time, and which pieces are fixed protocol constants vs per-account/per-session state. — 2026-09-10: steps 1–2 of C56 are recorded (bullet above); step 3 — key establishment + signing — is closed source: the public OpenAbsinthe-Stub repo is an official mock whose methods are stubs (its README states the real repository is closed source), so the spec records only the API contract: context creation consumes the certificate chain + hardware configuration and outputs the session-info-request blob; key establishment consumes the server response; signing produces the final blob with no further inputs (all state retained in the context) [RP:macos.rs], [RP:activation.rs], [RP:auth.rs], [OA]. Remaining: wire-verification of the blob against one live activation; the enc-variant derivation rides C57. — 2026-09-11 (rev 14 extraction): the five obfuscation labels are now MAPPED to their fields (previously only listed together): serial → `Gq3489ugfi`, platform UUID → `Fyp98tpgj`, root-disk UUID → `kbjfrfpoJU`, ROM → `oycqAZloTNDm`, MLB → `abKPld1EcMni` [OA:nac.rs]; the derivation producing each `_enc` byte variant is still closed source. The hardware-configuration field set is recorded in full: product name (Apple product-type form, e.g. `Macmini9,1`), MAC address (exactly 6 raw bytes), platform serial, platform UUID, root-disk UUID, board ID, OS build number, ROM (raw bytes), MLB, plus the five `_enc` raw-byte variants [OA:nac.rs]. Step 3's closure is confirmed at the source level: the crate holding the key-establishment math ships as a closed-source stub whose mock methods panic; only the API contract is observable [OA:README.md], [OA:nac.rs].
- The helper's export-code format (2026-09-11, rev 15 — **C58 closed** by facts-only extraction from the helper's public upstream repo, which is unlicensed — facts only): the payload is a 4-byte ASCII magic `OABS`, a one-byte flag (1 = "prevent sharing" mode, 0 otherwise), then a raw protobuf message (no length prefix; protobuf starts at offset 5). Protobuf field map: outer — 1 nested identifier message, 2 macOS product version (sysctl `kern.osproductversion`), 3 protocol version (hardcoded `1640`), 4 device id (the IOPlatformUUID again), 5 a hardcoded iCloudHelper user-agent string, 6 a hardcoded AOSKit version string; nested identifier message — 1 product name (e.g. `MacBookPro18,3`), 2 MAC address (6 raw bytes), 3 platform serial, 4 platform UUID, 5 root-disk UUID, 6 board ID (Apple Silicon: literal prefix `Mac-` + lowercase hex of the device-tree board-id bytes), 7 OS build number (sysctl `kern.osversion`), 8/9/10/12/14 the five `_enc` obfuscated blobs read from the `IOPower:/` registry under Apple's key names (`Gq3489ugfi` serial, `Fyp98tpgj` platform UUID, `kbjfrfpoJU` root-disk UUID, `oycqAZloTNDm` ROM, `abKPld1EcMni` MLB — matching §1.1's label mapping), 11 ROM (Intel: the device-tree options ROM value; Apple Silicon: last 6 bytes of SHA-256 of `/chosen` `unique-chip-id`), 13 MLB string. Two delivery modes: plain = standard base64 of the payload (clipboard text, or the raw payload bytes as a QR code at medium error correction); restricted = AES-256-CBC/PKCS7 over the payload, ciphertext prefixed with ASCII `Salted__` + 8-byte random salt (legacy OpenSSL `enc` container), key/IV derived by the legacy EVP_BytesToKey KDF (repeated MD5 of previous-hash ‖ password ‖ salt to 48 bytes: first 32 = key, next 16 = IV) keyed by a human activation code (`MB` + 4×4 chars from the alphabet `ABCDEFGHJKLMNPQRSTUVWXYZ123456789`), then base64. The restricted mode additionally uploads the blob (JSON `data` + `id` = lowercase hex SHA-256 of the activation code) to the vendor's server (`hw.openbubbles.app/code`) — the ≤20-user/one-time/48-hour mechanics live SERVER-side; nothing is embedded in the payload (no checksum, signature, compression, or expiry). No public parser exists (the client apps are closed-source); the construction above is the parse specification [MHG:ViewController.swift], [MHG:HwIdentifiers.swift], [MHG:mac_hw_info.pb.swift].
- The validation blob is submitted as the `validation-data` field of the IDS register body and, base64, in the `X-Mme-Nas-Qualify` header of the iCloud account login [RP:ids/user.rs], [RP:auth.rs]. `TODO(capture):` C5 — exact byte shape of the blob a genuine Mac produces, and the current exchange over the two validation bag URLs.
- The validation blob is submitted as the `validation-data` field of the IDS register body and, base64, in the `X-Mme-Nas-Qualify` header of the iCloud account login [RP:ids/user.rs], [RP:auth.rs]. `TODO(capture):` C5 — exact byte shape of the blob a genuine Mac produces, and the current exchange over the two validation bag URLs. — 2026-10-04 (rev 26, live): **REPLAY GUARD — one mint = one device record.** Apple keys a validation blob's consumption server-side: re-submitting an already-consumed blob at the sign-in/register pair answers HTTP 409, which is a REPLAY refusal, NOT a blob rejection — a fresh blob under the same device identity succeeds where the reused one failed, and a captured blob of external provenance succeeds outright (session 17: a captured stamp replaced the local mint and the 409 vanished). Client consequences: mint or fetch a fresh blob per registration attempt (the 15-minute validity above makes stale reuse doubly fatal), and mint the sign-in's blob AT SIGN-IN TIME — never a login-start blob carried across the 2FA wait. A caller-injected blob from any source (local mint, relay Mac, staged capture) is a legitimate stamp; the injection points are the register body's `validation-data` and the sign-in's `X-Mme-Nas-Qualify` header.
- Hardware-related anisette headers seen in activation flows: `X-Apple-I-SRL-NO` (serial), `X-Apple-I-MLB` (logic-board serial), `X-Apple-I-ROM` (hex ROM, lowercase) [RP:relay.rs], [APA:anisette]. `TODO(capture):` C6 — the full anisette header set a current client actually sends, including which optional hardware headers appear. — 2026-09-10: the header-set half of C6 is closed by facts-only extraction: per-request anisette headers `X-Apple-I-Client-Time`, `X-Apple-I-TimeZone`, `X-Apple-Locale`, `X-Apple-I-MD-RINFO`, `X-Mme-Device-Id`, `X-Apple-I-MD` (base64 OTP), `X-Apple-I-MD-M` (base64 machine ID), plus per-platform hardware headers — macOS: `X-Apple-I-MLB`, `X-Apple-I-ROM` (lowercase hex), `X-Apple-I-SRL-NO` [APA:omnisette], [APA:icloud-auth], [RP:relay.rs]. They are produced by an ADI provisioning exchange with gsa.apple.com (start/finish provisioning returning machine tokens), then a local OTP [APA:omnisette]. GSA context (cross-referenced with C6): the Albert activation itself needs NO Apple-Account login — only the FairPlay-signed activation-info plist (§1.2); the GSA login (full Apple-ID password, SRP over gsa.apple.com/grandslam/GsService2, s2k/s2k_fo PBKDF2, 2FA-capable) feeds the delegate logins that yield the IDS `auth-token`/`profile-id` and the PET token [RP:auth.rs], [APA:icloud-auth]. The extraction confirms the validation blob rides base64 in the `X-Mme-Nas-Qualify` header of the iCloud sign-in POST (recorded above) [RP:auth.rs]. — 2026-09-11 (rev 14 extraction round over the anisette crates): the value-format and provisioning halves of C6 are closed. Per-request value forms: `X-Apple-I-Client-Time` = UTC now truncated to whole seconds, RFC 3339/ISO-8601 with the offset suffix replaced by `Z` (provisioning requests send the raw offset form without the Z — a recorded inconsistency between code paths); `X-Apple-I-TimeZone` = literal `UTC` (not the device zone); `X-Apple-Locale` = `en_US` hardcoded (a companion `X-Apple-I-Locale: en_US` variant exists on the browser-style header set); `X-Apple-I-MD-RINFO` = verbatim passthrough of the string returned in the finish-provisioning response; `X-Mme-Device-Id` = lowercase hyphenated UUID string of 16 locally random bytes (`keychain_identifier`); `X-Apple-I-MD-M` = base64 (standard, padded) of the 60-byte `mid`; `X-Apple-I-MD-LU` = lowercase hex (no dashes) SHA-256 over the same 16 bytes (real macOS instead uses AuthKit's local-user UUID) — present in some header sets, omitted in others (disagreement — C57). Header sets are cached 60 s; a fresh OTP is generated per cache miss. The ADI provisioning flow: `GET https://gsa.apple.com/grandslam/GsService2/lookup` → XML plist whose `urls` keys `midStartProvisioning`/`midFinishProvisioning` give the endpoint URLs (never hardcoded); POST to the start URL with an XML plist body of two empty top-level dicts `Header` and `Request` → response `Response` → `spim` (base64); a local session is built from `spim` (+ empty hostuuid, DSID `-2`, flavor `Mac`=1 / `IOS`=3, flavor chosen by whether the client-info string contains `iPhone OS`) yielding `cpim` (base64); POST to the finish URL with `Request` = {`cpim`} → response `Response` → `ptm`, `tk` (both base64) and `X-Apple-I-MD-RINFO`; finishing the session with the decoded `tk`/`ptm` yields the machine state: `client_secret` (32 bytes), `mid` (60 bytes, Apple-issued at provisioning — reused for every OTP until re-provisioning, which happens only on a not-provisioned failure, e.g. ADI error -45061), `metadata` (variable blob). Provisioning HTTP specifics: `Content-Type: application/x-www-form-urlencoded` despite the XML-plist body (intentional), title-cased header names forced, HTTP/1.1, Apple root CA pinned (embedded DER), and a fixed companion header set on all three requests: akd `User-Agent`, `X-Apple-Baa-E: -10000`, `X-Apple-Baa-Avail: 2`, `X-Apple-Baa-UE: AKAuthenticationError:-7066|com.apple.devicecheck.error.baa:-10000`, `X-Apple-Host-Baa-E: -7066`, `X-Apple-Client-App-Name: akd`, `Accept: */*`, `Accept-Language: en-US,en;q=0.9`, plus `X-Apple-I-MD-LU`, `X-Mme-Device-Id`, `X-Mme-Client-Info`, `X-Apple-I-Client-Time` and any caller-supplied hardware headers. State persists as an XML plist with `keychain_identifier` (data) + the provisioned fields (`client_secret`, `mid`, `metadata`, `rinfo`, `flavor`) [APA:omnisette]. CLOSED-SOURCE RESIDUE: the OTP (`X-Apple-I-MD`) and trusted-device 2FA-code derivations live inside a closed-source crate — its public repo is a stub with `todo!()` methods; recorded is only the call contract: inputs (32-byte `client_secret`, 60-byte `mid`, `metadata`, flavor) → raw OTP bytes (base64 into the header) / 32-bit 2FA code [OA-stub], [APA:omnisette]. — rides C57/C56 residue; the seam stays injectable in the code.
### 1.2 Activation endpoints and certificates
@@ -30,7 +30,7 @@
- Bag shape and fetching (second-extraction facts): the live bag is fetched over GET from `https://init.ess.apple.com/WebObjects/VCInit.woa/wa/getBag?ix=3`; the response body is a plist dictionary with a single `bag` key whose value is embedded plist data — those bytes are themselves a plist parsed into a flat bag-key → URL dictionary [RP:util.rs:57-81]. The fetch is cached for the process lifetime keyed by bag URL; there is no TTL or refresh, and a missing key inside the parsed bag is its own error [RP:util.rs:59-81]. The APNs bag is fetched over plain HTTP from `http://init-p01st.push.apple.com/bag` and resolves integer `APNSCourierHostcount` and string `APNSCourierHostname` [RP:util.rs:56], [RP:aps.rs:1223-1224].
- The second implementation never fetches a bag at runtime: all endpoint URLs are compile-time pinned, and the bag fetch URL is only mentioned as the manual refresh source. Its pinned URLs: DS-ID authentication at `https://profile.ess.apple.com/WebObjects/VCProfileService.woa/wa/authenticateDS`, phone-number authentication at `https://identity.ess.apple.com/WebObjects/TDIdentityService.woa/wa/authenticatePhoneNumber`, dependent registrations at `.../TDIdentityService.woa/wa/getDependentRegistrations`, handles at `.../VCProfileService.woa/wa/idsGetHandles`, lookup at `https://query.ess.apple.com/WebObjects/QueryService.woa/wa/query` (used only inside the APNs HTTP tunnel, never plain HTTPS), registration at `https://identity.ess.apple.com/WebObjects/TDIdentityService.woa/wa/register` [IG:ids/bag.go:19-28], [IG:ids/urls.go:19-23], [IG:lookup.go:519-527]. It has no support at all for the alias-provisioning, spam-report, validation-certificate, validation-initialization, or multi-user phone-auth endpoints [IG:ids/bag.go, repo-wide].
- IDS HTTPS requests in both implementations use a custom TLS trust pool containing only embedded Apple root certificates, not the system store [RP:util.rs:208-242], [IG:util/appletls/util.go]. — 2026-09-10 [CAP-BAG]: the live bag was fetched directly with an unauthenticated `GET https://init.ess.apple.com/WebObjects/VCInit.woa/wa/getBag?ix=3` — no auth, no anisette; the response is a plist with `signature`, `certs`, and `bag` keys, the inner bag a flat dictionary of **405 entries** (full response archived on file as `ids-bag-2026-09-10.xml`). This closes the live-bag-contents half of C1 without an instrumented client. Notably the endpoint presented a TLS chain the local system store rejected (consistent with the sources' Apple-root-only pinning). Every id-* URL that either source pins (`id-register`, `id-query`, `id-authenticate-ds-id`, `id-authenticate-phone-number`, `id-authenticate-multiple-users`, `id-get-dependent-registrations`, `id-get-handles`, `id-validation-cert`, `id-initialize-validation`, …) matches the live bag — no drift. The bag also carries the scalar `id-query-refresh-queued-query-interval: 28800` (8 h, queued-refresh interval). `TODO(capture):` C47 — the concrete plist encoding of both bag responses, and of the embedded `bag` payload those responses carry, is not stated in this fact set (the archives are named `.xml`, and that is the whole of the evidence here); neither is the response content type. — 2026-09-10 [CAP-BAGCT]: the encoding half of C47 is settled. `GET https://init.ess.apple.com/WebObjects/VCPermsService.woa/wa/getBag?ix=3` (the `VCPermsService.woa` app also serves the bag — [CAP-BAG] had used `VCInit.woa`) returned `Content-Type: text/xml`, 94,859 bytes, an XML plist, top-level keys `signature`/`certs`/`bag`; the embedded `bag` value is base64 of an XML plist (decodes to `<?xml version="1.0"…<plist version="1.0"><dict>…`). The APNs bag (`GET http://init-p01st.push.apple.com/bag`) returned `Content-Type: application/x-apple-plist` but the body is still an XML plist, 10,675 bytes, byte-identical across two same-day fetches. Verdict: both bag responses and the embedded bag payload are XML plists in every observed case — no binary plist on the bag wire; the content-type labels are unreliable (`text/xml` vs `application/x-apple-plist` with XML bodies) and parsing goes by content, not label. Also [CAP-IDSTLS]: `openssl s_client` with bare-hostname SNI to `init.ess.apple.com:443` and `query.ess.apple.com:443` — init serves leaf `CN=init.ess.apple.com` ← `Apple Server Authentication CA 2` ← `Apple Root CA - G3`; query serves leaf `CN=query.ess.apple.com` ← `Apple Server Authentication CA` (the older CA) ← `Apple Root CA`; neither root is served, and the unanchored chain is rejected by the system store (OpenSSL verify code 20; curl error 60 with the system bundle) — matching the Apple-root-only pool of the sources. Anchor set to embed: `Apple Root CA - G3` and `Apple Root CA` — Apple roots, distinct from the publicly-trusted Comodo/AAA root the courier chain needs ([CAP-COURIERTLS], §3.1); pin roots only, never the leaf. Both root DERs are committed as `ids/src/main/resources/imessage/AppleRootCA-G3.cer` and `AppleRootCA.cer` (G3 from Apple's live CA page; the G1 `Apple Root CA` is no longer published by Apple — sourced from the public CT log, crt.sh id 1160190, SHA-256 `B0:B1:73:0E:CB:C7:FF:45:05:14:2C:49:F1:29:5E:6E:DA:6B:CA:ED:7E:2C:68:C5:BE:91:B5:A1:10:01:F0:24`; G3 SHA-256 `63:34:3A:BF:B8:9A:6A:03:EB:B5:7E:9B:3F:5F:A7:BE:7C:4F:5C:75:6F:30:17:B3:A8:C4:88:C3:65:3E:91:79`) and each captured intermediate openssl-verifies against its root.
- The Albert activation request is a form post containing an activation-info plist (activation randomness, activation state, build version, a DER certificate request, device class "MacOS", product type, product version, serial number, and a unique device ID) plus a FairPlay certificate chain and a FairPlay signature over the activation-info bytes [RP:activation.rs]. The certificate request is generated over a fresh 1024-bit RSA key with a "Client Push Certificate" common name [RP:activation.rs]; a second implementation agrees on the 1024-bit RSA push key (2048 also works) and notes a generator-version field [IG:pushcert]. The response embeds a plist whose activation record carries the device certificate (plus chain) [RP:activation.rs], [IG:pushcert]. `TODO(capture):` C8 — the exact activation-info field set and FairPlay certificate choice a genuine current client sends (one source reports signing with one of ten bundled FairPlay keys [RP:activation.rs]). — 2026-09-10: the wire half of C8 is closed by facts-only extraction (both source repos). The request is a POST (form-urlencoded) with a single form key `activation-info` whose value is an XML plist carrying `ActivationInfoComplete` (true), `ActivationInfoXML` (a nested XML plist), `FairPlayCertChain`, and `FairPlaySignature`; no timestamp is sent in the request [RP:activation.rs], [IG:activationinfo.go]. Nested activation-info keys: `ActivationRandomness` (uppercase UUIDv4), `ActivationState` ("Unactivated"), `BuildVersion`, `DeviceCertRequest` (PEM CSR), `DeviceClass`, `ProductType`, `ProductVersion`, `SerialNumber`, `UniqueDeviceID`. The CSR is 1024-bit RSA, CN "Client Push Certificate", OU "iPhone", O "Apple Inc.", signed SHA-1WithRSA. FairPlay signing covers the serialized ActivationInfo plist (SHA-1, RSA PKCS1v15) and yields the (chain, signature) pair placed into the activation body; both sources embed FairPlay identities (Go: two embedded files; Rust: ten certificate/key pairs, one chosen at random per activation — provenance unrecorded, → C57) [IG:fairplay.go], [RP:activation.rs]. The response's outer XML `<Protocol>` block is plist-parsed: `device-activation` → `activation-record` → `DeviceCertificate` (PEM), plus `ack-received` and `show-settings` — only the device certificate returns; the private key is client-generated and never sent, and no device token appears in the response [IG:pushcert], [RP:activation.rs]. Courier link: the DeviceCertificate (with the client private key) becomes connect field 12 (§3.3), and the connect nonce is signed with that key — Go: a 20-byte random nonce with the first byte zeroed; Rust: 0x00 ‖ big-endian unix-milliseconds ‖ 8 random bytes; signature = 0x01 0x01 ‖ RSA-SHA1(nonce) (both) [IG:connection.go], [RP:aps.rs] (nonce-shape tension with §3.3's recorded agreement — C57).
- The Albert activation request is a form post containing an activation-info plist (activation randomness, activation state, build version, a DER certificate request, device class "MacOS", product type, product version, serial number, and a unique device ID) plus a FairPlay certificate chain and a FairPlay signature over the activation-info bytes [RP:activation.rs]. The certificate request is generated over a fresh 1024-bit RSA key with a "Client Push Certificate" common name [RP:activation.rs]; a second implementation agrees on the 1024-bit RSA push key (2048 also works) and notes a generator-version field [IG:pushcert]. The response embeds a plist whose activation record carries the device certificate (plus chain) [RP:activation.rs], [IG:pushcert]. `TODO(capture):` C8 — the exact activation-info field set and FairPlay certificate choice a genuine current client sends (one source reports signing with one of ten bundled FairPlay keys [RP:activation.rs]). — 2026-09-10: the wire half of C8 is closed by facts-only extraction (both source repos). The request is a POST (form-urlencoded) with a single form key `activation-info` whose value is an XML plist carrying `ActivationInfoComplete` (true), `ActivationInfoXML` (a nested XML plist), `FairPlayCertChain`, and `FairPlaySignature`; no timestamp is sent in the request [RP:activation.rs], [IG:activationinfo.go]. Nested activation-info keys: `ActivationRandomness` (uppercase UUIDv4), `ActivationState` ("Unactivated"), `BuildVersion`, `DeviceCertRequest` (PEM CSR), `DeviceClass`, `ProductType`, `ProductVersion`, `SerialNumber`, `UniqueDeviceID`. The CSR is 1024-bit RSA, CN "Client Push Certificate", OU "iPhone", O "Apple Inc.", signed SHA-1WithRSA. FairPlay signing covers the serialized ActivationInfo plist (SHA-1, RSA PKCS1v15) and yields the (chain, signature) pair placed into the activation body; both sources embed FairPlay identities (Go: two embedded files; Rust: ten certificate/key pairs, one chosen at random per activation — provenance unrecorded, → C57) [IG:fairplay.go], [RP:activation.rs]. The response's outer XML `<Protocol>` block is plist-parsed: `device-activation` → `activation-record` → `DeviceCertificate` (PEM), plus `ack-received` and `show-settings` — only the device certificate returns; the private key is client-generated and never sent, and no device token appears in the response [IG:pushcert], [RP:activation.rs]. Courier link: the DeviceCertificate (with the client private key) becomes connect field 12 (§3.3), and the connect nonce is signed with that key — Go: a 20-byte random nonce with the first byte zeroed; Rust: 0x00 ‖ big-endian unix-milliseconds ‖ 8 random bytes; signature = 0x01 0x01 ‖ RSA-SHA1(nonce) (both) [IG:connection.go], [RP:aps.rs] (nonce-shape tension with §3.3's recorded agreement — C57). — 2026-10-02/03 (rev 26, live): three request/response refinements. (1) `DeviceCertRequest` rides as a plist `<data>` whose content is base64 of the PEM CSR TEXT bytes — a DER-encoded CSR is rejected server-side (the SIGNATURE_VERIFICATION_FAILED page; the earlier "DER `<data>`" reading was dead, session 12). (2) The accepted `FairPlayCertChain` is THREE DER certificates concatenated — leaf + FairPlay CA + Apple Root CA (the two CA links captured in the accepted Android request); leaf-only and leaf+CA were refused. (3) The activation response may wrap the `<Protocol>` block inside a larger document (`<Document><Protocol>…</Protocol><ScrollView>…` whose UI XML breaks a whole-document parse, live 2026-10-02) — a client extracts the `<Protocol>` span and parses it alone; a bare `<Protocol>` document parses as before. Refusals (no DeviceCertificate) carry the reason in wrapped-UI page text, not in a structured field.
- Account login requires an authentication token from Apple ID username and password. With 2FA there are two retrofitted options: legacy (2FA code appended to the password) and GrandSlam, where "anisette data" proves device identity so no repeated 2FA entry is needed; the result is a Password Equivalent Token (PET) usable in place of password+2FA [JJ]. GrandSlam login uses an SRP-6a variant (3072-bit group) [APA:client], [IG:appleid]; the Apple ID login side uses `idmsa.apple.com` endpoints with session, scnt, and client-info headers [IG:appleid]. The iCloud setup login (`setup.icloud.com`) posts a delegates map and yields per-delegate service data; the IDS delegate yields an auth token and a profile id [RP:auth.rs].
- The short-lived authentication token must be exchanged for a longer-lived authentication certificate; this certificate allows IDS registration but is not sufficient for key lookups [JJ].
- IDS authentication certificates: for Apple-ID users the CSR is a fresh 2048-bit RSA key whose common name is a SHA-1 of the realm user id, submitted to the `id-authenticate-ds-id` bag URL [RP:auth.rs] (pinned URL variant: `profile.ess.apple.com` authenticateDS [IG:bag]); for phone-number users the realm id has the form "P:<number>" against `id-authenticate-phone-number` [RP:auth.rs] (pinned variant: `identity.ess.apple.com` authenticatePhoneNumber [IG:bag]). A SIM-less phone route (`id-authenticate-multiple-users`) carries a per-request carrier EAP-AKA signature produced through the carrier's entitlement server, with the APNs connect token as client nonce [RP:auth.rs], [RP:util.rs]. `TODO(capture):` C7 — the carrier entitlement exchange per carrier (request/response shape varies by carrier). `TODO(capture):` C54 — the authenticate exchange's unrecorded wire shape: the request body's plist keys, the CSR's encoding ("CSR" is the recorded term; nothing states PKCS#10 or another form) and its self-signature algorithm, the encoding of the CN's SHA-1 (raw digest vs hex vs base64), whether the §1.4 signature header sets are carried, and the response's wire keys (the short-lived token, the longer-lived authentication certificate, and their relationship). — 2026-09-10: C54 is closed by facts-only extraction (both source repos), with three disagreements tagged for capture (C57). Endpoints: bag keys `id-authenticate-ds-id` / `id-authenticate-phone-number`, one authentication per user; Rust resolves them from the bag, Go pins the full URLs — a recorded client-design disagreement (bag resolution vs pinned URLs), not a wire fact; Rust also has `id-authenticate-multiple-users` for the SMS-less flow (recorded above) [RP:ids/user.rs], [IG:bag.go]. Request body keys (agreed): `authentication-data`, `csr`, `realm-user-id`; the CSR is DER (both). `authentication-data` holds `auth-token` for the Apple/DS flow and `push-token` + `sigs` (array) for the phone flow. The SMS-less variant body (Rust only): `csr`, `sig`, `tag`, `user-id`. Self-signature over the CSR DISAGREES: Rust SHA-1 with RSA/PKCS1, Go SHA256WithRSA — `TODO(capture):` (C57). CN encoding: a hex STRING of the SHA-1 digest of the realm-user-id (not raw, not base64); Rust uppercases the hex, Go lowercases — case-only disagreement, `TODO(capture):` (C57). The §1.4 signature header sets are NOT applied to authenticate in either repo; the headers sent are only `user-agent` and `x-protocol-version` (Rust adds `content-encoding: gzip`) [RP:auth.rs], [IG:authdevice.go]. Response keys: `status`, `cert` — flat, no per-user nesting (both); the multiple-users variant returns an `authentication_responses` array with per-entry `cert`, `user-id`, `status` (Rust only) [RP:auth.rs], [IG:authdevice.go].
@@ -61,10 +61,11 @@
- Facts-only extraction (2026-09-11, rev 14) over the GSA-auth crate and the Mac client's auth flow. Endpoints: the two SRP steps both POST `https://gsa.apple.com/grandslam/GsService2` with `Content-Type: text/x-xml-plist`; the body is an XML plist with top-level `Header` (only `Version` = `1.0.1`) and `Request`. Init request keys: `A2k` (client SRP public value, plist data), `cpd` (a client-provided-data dictionary, below), `o` = `init`, `ps` = [`s2k`, `s2k_fo`], `u` = username. Init response (under `Response`): `s` (salt, data), `B` (server public value, data), `i` (iteration count, integer), `c` (challenge string), optional `sp` (selected protocol, default `s2k`), `Status` (dict with integer `ec`, 0 = ok, and string `em`). Complete request keys: `M1` (proof, data), `c`, `cpd`, `o` = `complete`, `u`. Complete response: `M2` (server proof, data), `spd` (encrypted plist data), `Status` whose `au` string selects secondary auth: `secondaryAuth` (SMS/trusted-phone) or `trustedDeviceSecondaryAuth` [APA:icloud-auth].
- SRP-6a variant (byte-exact): 2048-bit group, g = 2, SHA-256 throughout. Password key: SHA-256 of the raw password; for `s2k_fo` that digest is ASCII-lowercase-hex-encoded (64 chars) and those bytes feed PBKDF2, for `s2k` the raw 32-byte digest feeds it; PBKDF2-HMAC-SHA256 with server salt `s` and server iteration count `i`, output 32 bytes = the SRP password. Identity hash quirk (GSA-specific, must match): the identity hash uses an EMPTY username — SHA-256(empty ‖ ":" ‖ password-buffer) — while M1's H(username) term uses the REAL username. x = SHA-256(salt ‖ identity-hash); u = SHA-256(A ‖ B); k = SHA-256(N ‖ g padded to N's length); premaster = (B − k·g^x)^(a + u·x) mod N; K = SHA-256(premaster big-endian); M1 = SHA-256(XOR(H(N), H(pad(g))) ‖ H(username) ‖ salt ‖ A ‖ B ‖ K); M2 = SHA-256(A ‖ M1 ‖ K). A is sent as `A2k`; the received `M2` is verified against the computed M2 [APA:icloud-auth].
- `spd` decryption: AES-256-CBC + PKCS7; key = HMAC-SHA256(key = K, message = ASCII `extra data key:`), IV = first 16 bytes of HMAC-SHA256(key = K, message = ASCII `extra data iv:`). Decrypted plist carries: a per-service token map `t` (service id → token + expiry in epoch-ms or duration in seconds, e.g. `GsIdmsToken`), `acname`, `DsPrsId`, `adsid`, first/last name [APA:icloud-auth].
- Anisette at login: the anisette base headers (§1.1) ride the POSTs filtered to `X-Apple-I-MD-LU`, `X-Apple-I-MD-RINFO`, `X-Apple-I-MD-M`, `X-Apple-I-MD`, `X-Mme-Device-Id`, plus akd-variant `X-MMe-Client-Info`/User-Agent and `X-Apple-AK-Context-Type`/`X-Apple-Client-App-Name`/`X-Apple-I-Client-Bundle-Id`. The `cpd` dictionary carries anisette values as KEYS inside the body: `X-Apple-I-Client-Time`, `X-Apple-I-MD`, `X-Apple-I-MD-LU`, `X-Apple-I-MD-M`, `X-Apple-I-MD-RINFO`, `X-Mme-Device-Id`, optional `ptkn` (APNs token), plus fixed values: `X-Apple-I-Device-Configuration-Mode` = `0`, `X-Apple-I-Request-UUID` (uppercase UUIDv4, one per login, reused across both steps), `X-Apple-Requested-Partition` = `0`, `X-Apple-Security-Upgrade-Context` = `com.apple.authkit.generic`, `capp`, `cbid`, `cou` = `US`, `loc` = `en_US`, `svct` (e.g. `imessage`), and booleans `X-Apple-Offer-Security-Upgrade`=true, `at`=0, `bootstrap`=true, `ckgen`=true, `fcd`=true, `icdrsDisabled`=false, `icscrec`=true, `pbe`=false, `prkgen`=true, `webAccessEnabled`=false [APA:icloud-auth].
- 2FA: `au` = `secondaryAuth` → `GET https://gsa.apple.com/auth` returns JSON `AuthenticationExtras` (trustedPhoneNumbers with `numberWithDialCode`/`lastTwoDigits`/`pushMode`/`id`; HTTP 201 = SMS already sent); request a code with PUT `.../auth/verify/phone` body `{phoneNumber:{id}, mode:"sms"}`; verify with POST `.../auth/verify/phone/securitycode` (same body + `securityCode:{code}`). `au` = `trustedDeviceSecondaryAuth` → trigger the code push with `GET .../auth/verify/trusteddevice`, then submit: `GET .../GsService2/validate` with the code in a `security-code` request header. All 2FA requests carry `X-Apple-Identity-Token` = base64 of `adsid:GsIdmsToken` (from `spd`) and a large browser-style fixed header set. Success returns tokens in RESPONSE HEADERS: repeated `X-Apple-GS-Token` (one per GSA service), `X-Apple-HB-Token`, `X-Apple-PE-Token` — each base64 of colon-separated `ID:TOKEN[:...]` with inconsistent duration/expiration trailer forms (epoch-ms vs seconds, heuristically parsed) [APA:icloud-auth]. After a successful 2FA, the full SRP login is run a SECOND time, which then completes as logged-in [APA:icloud-auth]. An on-device variant exists (Circle PAKE over `.../GsService2/circle`: SRP-6a over the 3072-bit group with the DSID as username and the zero-padded 6-digit code as password, 16-byte random salt, DER/ASN.1 step payloads base64 in `pakedata`; the code arrives AES-128-GCM-encrypted over an SRP-key-derived channel with `recv->send`/`send->recv` info labels) [RP:auth.rs], [APA:icloud-auth]. — 2026-09-29 (rev 25, live): the trusted-device trigger is what makes the code APPEAR on the devices — it must run BEFORE prompting the user, and a SECOND trigger before the same validate invalidates the outstanding code and re-prompts every device (both observed live); `au` is returned inside the complete response's `Status` dict, not at the `Response` level.
- Anisette at login: the anisette base headers (§1.1) ride the POSTs filtered to `X-Apple-I-MD-LU`, `X-Apple-I-MD-RINFO`, `X-Apple-I-MD-M`, `X-Apple-I-MD`, `X-Mme-Device-Id`, plus akd-variant `X-MMe-Client-Info`/User-Agent and `X-Apple-AK-Context-Type`/`X-Apple-Client-App-Name`/`X-Apple-I-Client-Bundle-Id`. The `cpd` dictionary carries anisette values as KEYS inside the body: `X-Apple-I-Client-Time`, `X-Apple-I-MD`, `X-Apple-I-MD-LU`, `X-Apple-I-MD-M`, `X-Apple-I-MD-RINFO`, `X-Mme-Device-Id`, optional `ptkn` (APNs token), plus fixed values: `X-Apple-I-Device-Configuration-Mode` = `0`, `X-Apple-I-Request-UUID` (uppercase UUIDv4, one per login, reused across both steps), `X-Apple-Requested-Partition` = `0`, `X-Apple-Security-Upgrade-Context` = `com.apple.authkit.generic`, `capp`, `cbid`, `cou` = `US`, `loc` = `en_US`, `svct` (e.g. `imessage`), and booleans `X-Apple-Offer-Security-Upgrade`=true, `at`=0, `bootstrap`=true, `ckgen`=true, `fcd`=true, `icdrsDisabled`=false, `icscrec`=true, `pbe`=false, `prkgen`=true, `webAccessEnabled`=false [APA:icloud-auth]. — 2026-10-04 (rev 26, live): anisette is fresh PER SRP RUN, not per login flow — GSA rejects an SRP init whose `X-Apple-I-MD` was minted minutes earlier (error `ec` −22421), including the post-2FA re-run of the same login; the reference client mints a new set for every login attempt.
- 2FA: `au` = `secondaryAuth` → `GET https://gsa.apple.com/auth` returns JSON `AuthenticationExtras` (trustedPhoneNumbers with `numberWithDialCode`/`lastTwoDigits`/`pushMode`/`id`; HTTP 201 = SMS already sent); request a code with PUT `.../auth/verify/phone` body `{phoneNumber:{id}, mode:"sms"}`; verify with POST `.../auth/verify/phone/securitycode` (same body + `securityCode:{code}`). `au` = `trustedDeviceSecondaryAuth` → trigger the code push with `GET .../auth/verify/trusteddevice`, then submit: `GET .../GsService2/validate` with the code in a `security-code` request header. All 2FA requests carry `X-Apple-Identity-Token` = base64 of `adsid:GsIdmsToken` (from `spd`) and a large browser-style fixed header set. Success returns tokens in RESPONSE HEADERS: repeated `X-Apple-GS-Token` (one per GSA service), `X-Apple-HB-Token`, `X-Apple-PE-Token` — each base64 of colon-separated `ID:TOKEN[:...]` with inconsistent duration/expiration trailer forms (epoch-ms vs seconds, heuristically parsed) [APA:icloud-auth]. After a successful 2FA, the full SRP login is run a SECOND time, which then completes as logged-in [APA:icloud-auth]. An on-device variant exists (Circle PAKE over `.../GsService2/circle`: SRP-6a over the 3072-bit group with the DSID as username and the zero-padded 6-digit code as password, 16-byte random salt, DER/ASN.1 step payloads base64 in `pakedata`; the code arrives AES-128-GCM-encrypted over an SRP-key-derived channel with `recv->send`/`send->recv` info labels) [RP:auth.rs], [APA:icloud-auth]. — 2026-09-29 (rev 25, live): the trusted-device trigger is what makes the code APPEAR on the devices — it must run BEFORE prompting the user, and a SECOND trigger before the same validate invalidates the outstanding code and re-prompts every device (both observed live); `au` is returned inside the complete response's `Status` dict, not at the `Response` level. — 2026-10-04 (rev 26, live): the anisette riding the 2FA calls is fresh PER CALL (trigger and validate each re-mint; a set frozen at login start answers HTTP 434 — the frozen-OTP wall of session 16), the trusted-device trigger and the validate GET both carry `Content-Type: text/x-xml-plist` + `Accept: text/x-xml-plist`, and a failed verify's body is a plist whose `Status.ec`/`em` (or top-level `ec`/`em`) name the real failure — the HTTP status alone is opaque.
- PET: arrives as `X-Apple-PE-Token`; stored under service id `com.apple.gs.idms.pet`; default expiry 300 s when no trailer, otherwise parsed seconds/epoch-ms; refreshed by silently re-running the SRP login with the persisted password hash. Used as the HTTP Basic password (`username:PET`) on the iCloud requests below [APA:icloud-auth], [RP:auth.rs]. — 2026-09-29 (rev 25, live): the post-2FA SRP re-login's decrypted `spd` token map `t` is an ALTERNATIVE delivery channel carrying the PET plus the full per-service token set under the same `ID` keys (`com.apple.gs.idms.pet`, `com.apple.gs.icloud.auth`, `com.apple.gs.idms.hb`, `com.apple.gs.pb.auth`, `com.apple.gs.gpk.auth`, …21 observed in total); a no-2FA password login's `spd` carries only `com.apple.gs.appleid.auth` and NO PET — the header route on the 2FA-verification response and the re-login `spd` map are the two live-confirmed PET paths.
- Delegate logins: POST `https://setup.icloud.com/setup/signin/v2/login`, XML plist body with `delegates` (dictionary keyed by bundle id: `com.apple.private.ids` → {`protocol-version`: `4`}; `com.apple.mobileme` → {}), `protocolVersion` = `1.0`, `userInfo` = {`clientId` (uppercase UUIDv4), `language` = `en-US`, `timezone`}. Headers: Basic `username:PET`, `X-Mme-Nas-Qualify` = base64 validation blob (§1.1), `X-Apple-ADSID` = adsid, akd-flavor `X-Mme-Client-Info`, `User-Agent` = `com.apple.iCloudHelper/282 …`, full anisette set. Response plist: `status` (0 = ok), optional `localizedError` + `description`, `delegates` dictionary with per-entry `status`, `statusMessage`, `serviceData`. The IDS delegate's serviceData (kebab-case) yields `authToken` and `profileId` — exactly the IDS `auth-token` (sent as `authentication-data` = {`auth-token`: token}) and the `profile-id` (realm user id) that feed the authenticate exchange (§1.2) and registration (§1.4). The MobileMe delegate's serviceData yields a token map (incl. `mmeAuthToken`) and service configs [RP:auth.rs], [RP:macos.rs]. — 2026-09-29 (rev 25, live): the anisette headers minted at login start are SPENT by the GSA hops — the sign-in POST needs a FRESH set (a reused set was refused; the reference client mints per request); and the endpoint answered HTTP 409 with `ErrorID=ICLOUD_UNSUPPORTED_DEVICE` ("This Apple Account is valid but is not an iCloud account" / "Cannot create another iCloud account with this device") — a device-identity-bound iCloud-activation refusal (response plist carries `protocolVersion` 2, `ErrorID`, `Header`, `Body`, `DialogType`, `DefaultButton*`, `AlternateButton*`, `HelpLink`), consistent with the per-Mac-identity account cap the upstream docs put at ~20 accounts.
- Delegate logins: POST `https://setup.icloud.com/setup/signin/v2/login`, XML plist body with `delegates` (dictionary keyed by bundle id: `com.apple.private.ids` → {`protocol-version`: `4`}; `com.apple.mobileme` → {}), `protocolVersion` = `1.0`, `userInfo` = {`clientId` (uppercase UUIDv4), `language` = `en-US`, `timezone`}. Headers: Basic `username:PET`, `X-Mme-Nas-Qualify` = base64 validation blob (§1.1), `X-Apple-ADSID` = adsid, akd-flavor `X-Mme-Client-Info`, `User-Agent` = `com.apple.iCloudHelper/282 …`, full anisette set. Response plist: `status` (0 = ok), optional `localizedError` + `description`, `delegates` dictionary with per-entry `status`, `statusMessage`, `serviceData`. The IDS delegate's serviceData (kebab-case) yields `authToken` and `profileId` — exactly the IDS `auth-token` (sent as `authentication-data` = {`auth-token`: token}) and the `profile-id` (realm user id) that feed the authenticate exchange (§1.2) and registration (§1.4). The MobileMe delegate's serviceData yields a token map (incl. `mmeAuthToken`) and service configs [RP:auth.rs], [RP:macos.rs]. — 2026-09-29 (rev 25, live): the anisette headers minted at login start are SPENT by the GSA hops — the sign-in POST needs a FRESH set (a reused set was refused; the reference client mints per request); and the endpoint answered HTTP 409 with `ErrorID=ICLOUD_UNSUPPORTED_DEVICE` ("This Apple Account is valid but is not an iCloud account" / "Cannot create another iCloud account with this device") — a device-identity-bound iCloud-activation refusal (response plist carries `protocolVersion` 2, `ErrorID`, `Header`, `Body`, `DialogType`, `DefaultButton*`, `AlternateButton*`, `HelpLink`), consistent with the per-Mac-identity account cap the upstream docs put at ~20 accounts. — 2026-10-04 (rev 26, live): **the 409's true root cause is the validation-stamp REPLAY guard (§1.1), not the request shape or a blob rejection** — a captured stamp minted elsewhere passed the sign-in with HTTP 200 and CREATED the iCloud device record, and the full chain then completed: §1.2 authenticate + §1.4 register both green against live Apple, with a `mailto:` handle coming back ACTIVE (§1.3/§1.4). Two response/request refinements: (1) Apple's live sign-in response is KEBAB-CASE — the delegate entries carry `status`/`service-data`/`account-exists` (delegate keys observed live), i.e. `service-data` and `status-message`, NOT the extraction's camelCase `serviceData`/`statusMessage` (which survive as a fallback reading of the recording); (2) the sign-in body's client id rides the kebab-case wire key `client-id` in `userInfo` — the one body delta that survived until the live capture.
- The GSA `postdata` liveness event (rev 26, live 2026-10-03; `gsas.apple.com` per the host split above): a `POST .../grandslam/GsService2/postdata` with `Content-Type: text/x-xml-plist` and an XML-plist body of two top-level dictionaries — `Header` (empty) and `Request` — the `liveness` event declaring the device's services `icloud`, `imessage`, `facetime` (with recovery-key generation on), and two account-binding headers: `X-Apple-I-UrlSwitch-Info` = base64(`adsid:postdata`) and `X-Apple-HB-Token` = base64(`adsid:hb-token`) with the hb token from the post-2FA spd token map's `com.apple.gs.idms.hb` entry (§1.5 PET). The anisette set is the postdata-filtered machine/device subset (serial-bearing headers, locale and the request uuid are dropped); the akd-variant fixed headers ride. The APNs push token rides the body as `ptkn` (uppercase hex) when minted — a dead or absent token answers `ec` −800012 "Push token is invalid", so the Albert device certificate + courier connect token must be minted BEFORE the event, and the courier connection must stay OPEN across the whole registration: the connect-response token identifies the CONNECTION, and closing the socket between the mint and the postdata hop drew −800012 on an otherwise valid token. ORDER (correcting the earlier session note): the reference program runs the delegate sign-in FIRST and postdata AFTER it (the sign-in mints its own Nas-Qualify blob at sign-in time, §1.1) — the hop is a post-sign-in liveness declaration, not a sign-in precondition.
- Not present in these sources: no `idmsa.apple.com`, no `scnt`/session-token headers — the session role is played by `X-Apple-Identity-Token` (GSA requests) and Basic `username:PET` (iCloud requests); the idmsa-based flow recorded in §1.2's [IG] citations is a different client's route (C57 note) [APA:icloud-auth], [RP:auth.rs].
- — 2026-09-11 (rev 15 extraction: C59 closed). Group constants: the GSA login uses the RFC 5054 2048-bit MODP group, g = 2 (the standard Appendix-A prime); the Circle PAKE uses the RFC 5054 3072-bit group with g = 5 [APA:groups.rs], [RP:auth.rs]. The 2FA browser-style header set (all `/auth/verify/*` and `GsService2/validate` requests): the anisette base headers filtered to `X-Apple-I-MD-LU`/`X-Apple-I-MD-RINFO`/`X-Apple-I-MD-M`/`X-Apple-I-MD`/`X-Mme-Device-Id`, plus fixed literals `X-Apple-Client-App-Name`, `X-Apple-I-Client-Bundle-Id`, non-akd `X-MMe-Client-Info`, `X-Apple-I-CDP-Circle-Status: false`, `X-Apple-I-ICSCREC: true`, browser `User-Agent`, `Sec-Fetch-Site: same-origin`, `X-Apple-Requested-Partition: 0`, `X-Apple-I-DeviceUserMode: 0`, `X-Apple-I-Locale: en_US`, `X-Apple-Security-Upgrade-Context: com.apple.authkit.generic`, `Accept-Language: en-US,en;q=0.9`, `X-Apple-I-PRK-Gen: true`, `Sec-Fetch-Mode: cors`, `X-Apple-I-TimeZone: UTC`, `X-Apple-I-OT-Status: false`, `X-Apple-I-TimeZone-Offset: 0`, `X-MMe-Country: US`, `X-Apple-I-CDP-Status: false`, `X-Apple-I-Device-Configuration-Mode: 0`, `Sec-Fetch-Dest: empty`, `X-Apple-AK-Context-Type`, and `X-Apple-I-CFU-State` — a hardcoded base64 whose decoded XML plist is just an empty `<array/>` [APA:anisette.rs]. cpd values: `capp` = client app name, `cbid` = bundle id, `svct` = the AK context type — for the Messages flavor: `capp` = `Messages`, `cbid` = `com.apple.MobileSMS`, `svct` = `imessage` (AppleIDSettings flavor: `icloud` / `com.apple.systempreferences.AppleIDSettings`); `cou` = `US`, `loc` = `en_US`; `ptkn` = the APS push token as uppercase hex [APA:anisette.rs], [RP:lib.rs]. akd request values: `User-Agent` = literal `akd/1.0 CFNetwork/1494.0.7 Darwin/23.4.0`; `X-MMe-Client-Info` (akd form) = `<HardwareModel> <OSname;OSversion;OSbuild> <com.apple.AuthKit/1 (com.apple.akd/1.0)>` (observed `<iMac13,1> <macOS;13.6.4;22G513> …` and `<iPhone7,2> <iPhone OS;12.5.5;16H62> …`); `X-Apple-AK-Context-Type` = `imessage` or `icloud` per flavor [APA:lib.rs], [APA:gsa_auth.rs], [RP:lib.rs]. `spd` details: `GsIdmsToken` is a TOP-LEVEL string key (not inside `t`); `t` entries carry `token` (string) plus `expiry` (epoch-ms) or `duration` (seconds); account-name keys `fn`/`ln` (optional strings) [APA:client.rs]. Host split: `postdata` and `teardown` go to `gsas.apple.com` (with the S) with `X-Apple-I-UrlSwitch-Info` = base64(`adsid:postdata`/`adsid:teardown`); circle requests use `X-Apple-Identity-Token` when 2FA-authenticated, `X-Apple-HB-Token` = base64(`adsid:hbtoken`) otherwise; the app-token checksum is HMAC-SHA256 keyed with the session key over `apptokens` ‖ dsid ‖ app name [APA:client.rs]. Sign-in request values: `User-Agent` = `com.apple.iCloudHelper/282 CFNetwork/1408.0.4 Darwin/22.5.0` (static config constants); `userInfo.timezone` = a hardcoded IANA name (`America/New_York` in the reference config); `X-Mme-Client-Info` wraps the AOSKit form `com.apple.AOSKit/282 (com.apple.accountsd/113)` [RP:auth.rs], [RP:test.rs]. HTTP client notes: Apple root CA pinned, HTTP/1.1 title-cased headers forced, cookie store in use; a parser quirk makes the reference's composed sign-in UA literally `com.apple.iCloudHelper/282 com.apple.iCloudHelper/282` (duplicated token — treat the full static string as the recorded form) [RP:relay.rs]. — 2026-09-11 (small follow-up: the three remaining literals): the 2FA browser `User-Agent` = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko)` — static in every source, notably with NO Safari/Version token; the non-akd `X-MMe-Client-Info` for the browser set = the same device-bracket form with inner component `com.apple.akd/1.0 (com.apple.akd/1.0)` (rustpush's variant wraps `com.apple.AuthKit/1 (com.apple.MobileSMS/1262.500.151.1.2)`); `X-Apple-I-CFU-State`'s hardcoded base64 decodes to the FULL plist wrapper — `<?xml version="1.0" encoding="UTF-8"?>` + Apple DOCTYPE + `<plist version="1.0">` + `<array/>` + `</plist>` + trailing newline (182 bytes), NOT a bare `<array/>`; the sign-in `X-Mme-Client-Info` is bracketed like the others: `<HardwareModel> <macOS;13.6.4;22G513> <com.apple.AOSKit/282 (com.apple.accountsd/113)>`; and the sign-in UA quirk is confirmed — the composed header is the duplicated `com.apple.iCloudHelper/282 com.apple.iCloudHelper/282` while the full `com.apple.iCloudHelper/282 CFNetwork/1408.0.4 Darwin/22.5.0` string is the config literal other endpoints draw from [APA:anisette.rs], [APA:lib.rs], [APA:gsa_auth.rs], [RP:lib.rs], [RP:test.rs], [RP:macos.rs].
@@ -109,7 +110,7 @@
- Capture ground truth [CAP-COURIER]: a registered iPhone (iOS 26.1) connected to `courier2.push.apple.com`, with both ports 5223 and 443 in use — under interception the client alternated between them in a ~5–15 s reconnect loop. The courier pins against interception even with a trusted interception CA installed: the upstream TLS handshake is rejected with the OpenSSL error `bad extension`, i.e. the courier TLS stack is not vanilla TLS — consistent with a custom client-certificate/extension design, which arbitrates nothing between the [RP:aps.rs] (no client cert in handshake) and [IG:connection.go] (mutual TLS) readings; C18 stays open. Hosts observed pinned in the same window: `courier2.push.apple.com`, `gateway.icloud.com`, `configuration.apple.com`, `query.ess.apple.com`; with Apple hosts passed through, delivery works normally. A non-transparent HTTP-proxy capture sees no courier traffic at all — the courier bypasses the Wi-Fi HTTP proxy entirely.
- Transport is TCP port 5223 with TLS [RP:aps.rs], [IG:apns/connection.go]; the channel is forward-secret, and metadata such as timestamps and APNs routing information is not encrypted [APS-Send]. The client pins Apple's own root certificate(s) rather than system roots (a `profileidentity.ess.apple.com` root [RP:aps.rs]; a custom Apple root pool [IG:connection.go]).
- Client certificate: public write-ups say connecting requires a client certificate issued by the Albert activation server [JJ], [BM]; one source states the client certificate is NOT used in the TLS handshake and authentication happens in the application-layer connect message instead [RP:aps.rs]; the other configures mutual TLS with the Albert-issued certificate [IG:connection.go]. `TODO(capture):` C18 — whether the handshake presents a client certificate. — 2026-09-28 [PROBE-COURIER] (unauthenticated direct probe from this machine, no client certificate configured): the handshake to `courier.push.apple.com` COMPLETES on both 5223 and 443 (TLS 1.3, TLS_AES_256_GCM_SHA384, chain leaf ← `apple public server ecc ca 12 - g1`, anchored at `AAA Certificate Services`), and the server then sends a fatal TLS alert `certificate_required` on the first read — before any APNs frame can flow. C18 is settled by the server side: the courier uses TLS 1.3 **post-handshake client-certificate authentication** with the Albert-issued certificate; an application-layer-auth-only client would never get past the alert. The application-layer connect message (below) additionally carries the certificate+nonce+signature fields — both layers are in play; the client-cert presentation is the transport gate.
- ALPN: one source offers `apns-pack-v1` and `apns-security-v3` and switches to a compressed "packed" wire format when a pack protocol is negotiated [RP:aps.rs]; the other negotiates only `apns-security-v3` and speaks only the legacy frame format [IG:connection.go]. `TODO(capture):` C19 — negotiated ALPN and frame format in real traffic. (2026-09-10: the live ClientHello offers both `apns-security-v3` and `apns-pack-v1`; the ServerHello selects no ALPN protocol at all — so the "switch when a pack protocol is negotiated" trigger never fires in the observed session, and the legacy frame format is the default assumption [CAP-COURIER].)
- ALPN: one source offers `apns-pack-v1` and `apns-security-v3` and switches to a compressed "packed" wire format when a pack protocol is negotiated [RP:aps.rs]; the other negotiates only `apns-security-v3` and speaks only the legacy frame format [IG:connection.go]. `TODO(capture):` C19 — negotiated ALPN and frame format in real traffic. (2026-09-10: the live ClientHello offers both `apns-security-v3` and `apns-pack-v1`; the ServerHello selects no ALPN protocol at all — so the "switch when a pack protocol is negotiated" trigger never fires in the observed session, and the legacy frame format is the default assumption [CAP-COURIER].) — 2026-10-03 (rev 26, live, instrumented client): **C19 CLOSED.** Offering `apns-pack-v1:4096:4096` (the suffixed form), `apns-pack-v1`, and `apns-security-v3`, the server selects `apns-pack-v1:<enc>:<dec>` — observed `apns-pack-v1:4096:4096` on every courier host — and the packed frame format flows; the offer must include the exact suffixed form (a TLS stack that rejects a selection outside the offer otherwise refuses the handshake). With the pack-v1 ALPN the server's chain is leaf ← `Apple Server Authentication CA` ← Apple Root CA (the RSA chain; the working client pins the intermediate itself, distinct from the no-ALPN ECC chain of [CAP-COURIERTLS] anchored at `AAA Certificate Services`), and NO TLS client certificate is demanded on this path — the push certificate rides only in the connect frame (§3.3 field 12); the C18 `certificate_required` alert belongs to the no-ALPN legacy path.
- Handshake ground truth [CAP-COURIER] (pass-through pcap, 2026-09-10): the live client↔courier session is structurally standard TLS 1.3 — the server selects version 0x0304 with cipher TLS_AES_256_GCM_SHA384 — but the ClientHello carries Apple-private negotiation: a private supported-version value 0xfafa offered alongside TLS 1.3, a private elliptic-curve group 0x9a9a offered alongside the standard curves and the IANA X25519+ML-KEM-768 hybrid group (0x11EC; the offered key share, ~1263 B, is consistent with an ML-KEM-768 share), and two private extension types (0x5A5A, empty; 0x1A1A, one byte). Standard status-request, SCT, signature-algorithms, PSK-mode, and compress-certificate extensions are also offered. This refines the `bad extension` reading above: the courier is not a custom TLS protocol — it is TLS 1.3 plus Apple-private groups/extensions that conventional intercepting stacks do not offer or echo. Because TLS 1.3 encrypts all post-ServerHello handshake messages, whether the handshake presents the Albert client certificate (C18) is unobservable from a passive capture; that question now needs client-side evidence, not a better network vantage. (Note: the transport already negotiates a post-quantum hybrid key exchange even though message payloads ride the legacy envelope — PQ appears at the TLS layer today.)
@@ -118,7 +119,7 @@
### 3.2 Frame format
- Legacy frames: a 1-byte command, a 4-byte big-endian total length, then fields, each a 1-byte id, 2-byte big-endian length, and value [RP:aps.rs], [IG:apns/binary.go]. `TODO(capture):` C45 — the scope of the length prefix (fields-only, as in the documented APNs provider framing, vs the whole frame) and the per-command field-id tables are not captured in this spec (§7 item 15); the transport layer is buildable now, command builders are not. — 2026-09-10: C45 closed by facts-only extraction (both source repos): the 4-byte big-endian length covers the whole field block after the command byte (fields-only scope, agreed), and the per-command field-id tables are recorded in §3.3. The tagged table disagreements (connect flags, filter fields, command 14) and the Go-only extra fields ride C57; command builders are now writable from the spec, with captures arbitrating the tagged items.
- The packed format (single source) replaces the length/value fields with tag-based attributes with 4-bit value fields, extension bytes, and rolling 32-entry key/value caches; only a fixed list of short attribute keys is value-cached [RP:aps.rs]. `TODO(capture):` C19 — confirm packed format use and cache-size suffixes.
- The packed format (single source) replaces the length/value fields with tag-based attributes with 4-bit value fields, extension bytes, and rolling 32-entry key/value caches; only a fixed list of short attribute keys is value-cached [RP:aps.rs]. `TODO(capture):` C19 — confirm packed format use and cache-size suffixes. — 2026-10-03 (rev 26, live): **C19 CLOSED — the packed format's full wire shape is now recorded from the live connection** (it flowed with the negotiated `apns-pack-v1` ALPN, §3.1). A frame is a 1-byte command, the attribute-block length as a tagged value of width 8, then the attributes. Tagged value: a 1-byte tag whose low `width` bits carry an inline value while they are not all ones; at the all-ones marker the remainder follows as a 7-bit continuation varint (always present — even a zero remainder writes one 0x00 byte). Attribute: a 1-byte key tag — a single-byte key (< 0x10) rides inline as key|flags; longer keys go through the rolling key cache (0x20|flags|index reference; 0x10|flags + key-length−1 as a tagged width-4 value, then the key bytes) — then the value: inline integer (|0x80, ≤ 0x1f), wide integer ((byte-length−1)|0xa0, +0x08 negative, big-endian magnitude), data (tagged width 6), string (width 5, |0x40). Cache flags: 0x40 = first occurrence (full value, enters the rolling value cache), 0x80 = reference (tagged width-8 index into it). Both caches index from the newest entry; the key cache holds 32 entries, the value cache ~4 KiB of entries (the ALPN's `4096:4096` suffixes match the cache sizing). Post-connect sequence (live): connect → connect-response (token) → set-state (20) → filter (9) with the connect-response token plus the SHA-1 topic hashes as enabled topics — unfiltered connections are dropped within seconds, which invalidates the connect token before the login can use it.
### 3.3 Commands
@@ -267,7 +268,7 @@
Ordered by value. IDs match the inline `TODO(capture):` markers above.
1. **C25 — pair vs pair-ec vs PQ3 in current traffic.** The single most load-bearing question: do today's Apple clients still send "pair"/"pair-ec" envelopes, or has PQ3 replaced them entirely? Determines whether §4.2–4.3 are sufficient for receive compatibility. Second-extraction sharpening: Apple stated in 2024 that PQ3 "will fully replace the existing protocol within all supported conversations", but neither source repo has any post-2024 evidence, and one repo's fan-out code notes Apple senders shipping the same destination both with and without EC keys in one send (mixed schemes observed pre-PQ3) [IG:connector.go]. Payloads are end-to-end encrypted, so network captures can never settle this — the only path is an instrumented client. Observe: per-recipient `E` values (top-level and per-`dtl` entry) on outgoing and incoming messages; payload-less messages' `E` presence; whether current lookup results still carry the legacy/EC key sets beside the PQ3-registered Kyber-1024 + P-256 keys; the incoming validator final byte; incoming inner counters and gossip fields.
2. **C17 + C18 + C19 — APNs transport stack.** Courier hostname/count from the APNs bag (range 1–30 vs 1–50); whether the TLS handshake presents the Albert client certificate (mutual TLS) or auth happens only in the connect message; negotiated ALPN (`apns-security-v3` vs packed variants) and therefore legacy vs packed frame format. — 2026-09-10: `courier2.push.apple.com` observed live, 5223↔443 both used; vanilla-TLS interception rejected (`bad extension`) — Apple-private TLS negotiation confirmed (see §3.1). 2026-09-10 pcap mining: live session is TLS 1.3 (server picks 0x0304 / TLS_AES_256_GCM_SHA384) with Apple-private version 0xfafa, group 0x9a9a, extensions 0x5A5A/0x1A1A, and the X25519+ML-KEM-768 hybrid group offered; client offers both ALPNs, server selects none → legacy frame format assumed. C18 is unobservable by passive capture under TLS 1.3 — needs client-side evidence. — 2026-09-10 [CAP-APNSBAG]: the APNs bag was fetched directly (`GET http://init-p01st.push.apple.com/bag`, unauthenticated plain HTTP; archived on file as `apns-bag-2026-09-10.xml`): `APNSCourierHostname: courier.push.apple.com`, `APNSCourierHostcount: 50` — the 1–50 range is confirmed (`courier2` is one host within it); `APNSAllowTLS1_3: 1` is consistent with the observed TLS 1.3 session. C17's hostname/count half is closed; C18 still needs client-side evidence. [CAP-COURIER]
2. **C17 + C18 + C19 — APNs transport stack.** Courier hostname/count from the APNs bag (range 1–30 vs 1–50); whether the TLS handshake presents the Albert client certificate (mutual TLS) or auth happens only in the connect message; negotiated ALPN (`apns-security-v3` vs packed variants) and therefore legacy vs packed frame format. — 2026-09-10: `courier2.push.apple.com` observed live, 5223↔443 both used; vanilla-TLS interception rejected (`bad extension`) — Apple-private TLS negotiation confirmed (see §3.1). 2026-09-10 pcap mining: live session is TLS 1.3 (server picks 0x0304 / TLS_AES_256_GCM_SHA384) with Apple-private version 0xfafa, group 0x9a9a, extensions 0x5A5A/0x1A1A, and the X25519+ML-KEM-768 hybrid group offered; client offers both ALPNs, server selects none → legacy frame format assumed. C18 is unobservable by passive capture under TLS 1.3 — needs client-side evidence. — 2026-09-10 [CAP-APNSBAG]: the APNs bag was fetched directly (`GET http://init-p01st.push.apple.com/bag`, unauthenticated plain HTTP; archived on file as `apns-bag-2026-09-10.xml`): `APNSCourierHostname: courier.push.apple.com`, `APNSCourierHostcount: 50` — the 1–50 range is confirmed (`courier2` is one host within it); `APNSAllowTLS1_3: 1` is consistent with the observed TLS 1.3 session. C17's hostname/count half is closed; C18 still needs client-side evidence. [CAP-COURIER] — 2026-10-03 (rev 26): C19 CLOSED — with `apns-pack-v1` offered the server selects `apns-pack-v1:4096:4096` and the packed frame format flows; the packed chain anchors at the `Apple Server Authentication CA` intermediate (§3.1/§3.2), and no TLS client certificate is demanded on the packed path (refining C18: the post-handshake client-cert alert belongs to the no-ALPN legacy path only).
3. **C1 + C3 — registration request shape.** Second-extraction state: the bag fetch URL, nested-plist shape (`bag` key → embedded plist), one-per-process caching, and both clients' full endpoint sets are now known (§1.2) — rustpush resolves 11 bag keys at runtime with no fallbacks [RP]; the other pins everything and never fetches. What remains needs an instrumented client (pinned hosts bypass every proxy tier tried): the **live** bag contents (all `id-*` → URL mappings as served today), the current sub-service list and Messenger capability flags (17 vs 1), the capability-boolean set the server expects, and whether the server accepts both gzip-XML and plain-XML register bodies. — 2026-09-10: the bag contents are now observed directly — both bags fetched unauthenticated, no drift in the pinned URLs ([CAP-BAG]/[CAP-APNSBAG], §1.2). What remains C1/C3-bound to an instrumented client: the current sub-service list and Messenger capability flags (17 vs 1), the capability-boolean set the server expects, and whether the server accepts both gzip-XML and plain-XML register bodies — these live in the register request/response pair, which does require authentication (and Apple throttles abusive registrations, so no blind probing). C2 (protocol-version: "1640" vs "1660") and C4 (signature construction — structure now source-agreed, §1.4) also resolve from the same instrumented-client capture.
4. **C32 + C33 + C34 — semantic wire forms.** Tapback action codes (2006/2007/4000/1000/2 extensions) and target-string formats ("bp:" vs raw GUID vs "p:"-variants); typing start/stop encoding (empty payload vs body-field stop); whether command 102 carries a body (timestamp + read-up-to UUID) or none.
5. **C41 + C10 — lifetimes.** Real `next-hbi` values (and whether Apple sends it at all — one source parses it, the other doesn't read it), real identity-certificate lifetimes per handle type (the "~45 days" figure is now known to be a client-side cap, not an observed cert TTL — §6.1), real `session-token-expires-seconds`/`-refresh-seconds` values (one source consults them, the other ignores them), the effective renewal cadence, and which renewal trigger fires in practice (timer, private-IDS push 32/66, 6005, handle change). All reachable only via an instrumented client's debug log — the exact logging list is in §6.1.
@@ -330,6 +331,7 @@ Ordered by value. IDs match the inline `TODO(capture):` markers above.
## Revision history
- Rev 26 (2026-10-02→04, live registration of this protocol family against real Apple endpoints — Albert activation, APNs courier (packed ALPN), the full GSA chain through 2FA, and IDS authenticate + register all green; user-gated with a real Apple ID, `mailto:` handle ACTIVE): six live-verified fact groups beyond the extraction record. (1) **The 409's root cause is the validation-stamp REPLAY guard (§1.1)** — one mint = one device record: a consumed blob re-submitted answers HTTP 409 `ICLOUD_UNSUPPORTED_DEVICE`, a fresh (or captured-elsewhere) blob succeeds; the earlier readings (request shape, blob provenance) are dead. §1.2 authenticate + §1.4 register completed green after the stamp route was fixed, with the `mailto:` handle active (§1.3/§1.4). (2) **The courier's packed format is live (§3.1/§3.2, C19 CLOSED)** — the server selects `apns-pack-v1:4096:4096` and speaks the tag-based packed codec (full wire shape recorded from the wire); its chain anchors at the `Apple Server Authentication CA` intermediate, no TLS client certificate is demanded on that path, and the post-connect set-state + filter sequence is mandatory (unfiltered connections are dropped within seconds, invalidating the connect token). (3) **Apple's live GSA sign-in response is kebab-case** — delegate `service-data`/`status-message` (camelCase recordings become the fallback), and the sign-in body's client id rides `client-id` (§1.5). (4) **Anisette freshness is per-run and per-call** — fresh per SRP run incl. the post-2FA re-run (stale → `ec` −22421), fresh per 2FA call (frozen set → HTTP 434); the trigger/validate GETs carry the plist content type, and failed verifies name the real failure in the body plist's `Status.ec`/`em` (§1.5). (5) **The GSA `postdata` liveness event is recorded in full (§1.5)** — services `icloud`/`imessage`/`facetime`, `X-Apple-I-UrlSwitch-Info`/`X-Apple-HB-Token` binding, `ptkn` (−800012 with a dead push token — the courier connection must stay open across the registration), and the order sign-in FIRST, postdata AFTER (correcting the earlier reversed note). (6) **Albert activation refinements (§1.2)** — `DeviceCertRequest` is `<data>`(base64(PEM)), DER rejected; the accepted `FairPlayCertChain` is leaf + FairPlay CA + Apple Root CA concatenated; the response's `<Protocol>` block may arrive wrapped in a larger `<Document>` (parse the span); refusals state their reason in wrapped-UI page text. Code side: the packed courier codec + ALPN negotiation, `GsaPostdata`, the kebab-case parsing, the per-run/per-call anisette providers, the `<Protocol>`-span extraction, and the three-cert FairPlay chain all landed; the validation stamp stays caller-injected with the replay guard documented.
- Rev 25 (2026-09-29, first LIVE GSA login of this protocol family — SRP init/complete, both 2FA steps, and the PET-bearing re-login against `gsa.apple.com` with a real Apple ID, user-gated; the delegate sign-in POST was attempted and refused): the §1.5 chain is live-confirmed end-to-end up to the delegate sign-in, with five behavioral facts beyond the extraction record. (1) `au` (`trustedDeviceSecondaryAuth` observed) rides INSIDE the complete response's `Status` dict — reading it at the `Response` level silently turns a 2FA login into a "logged in, no PET" dead end (§1.5 2FA). (2) The PET's second live-confirmed delivery channel: the post-2FA re-login's decrypted `spd` token map `t` (21 service tokens observed incl. `com.apple.gs.idms.pet`), while a no-2FA login's `spd` carries only `com.apple.gs.appleid.auth` and no PET — an earlier implementation inference that a no-2FA login suffices for the PET is dead; 2FA is mandatory for the PET on this account class (§1.5 PET). (3) The trusted-device trigger creates the code on the devices and must precede the prompt; triggering twice before one validate invalidates the outstanding code and re-prompts (§1.5 2FA). (4) Anisette is one-time per request: headers minted at flow start are spent by the GSA hops, and the delegate sign-in with a reused set is refused — mint fresh per POST, matching the reference client's per-request provider (§1.5 delegate logins). Post-login ADI behavior confirmed live: after an account login the machine re-provisions for the personal DSID (GetLoginCode → not-provisioned → full exchange succeeds), which is the personal-`X-Apple-I-MD-M` path, not a defect. (5) The delegate sign-in answered HTTP 409 `ICLOUD_UNSUPPORTED_DEVICE` with the recorded error-plist shape (§1.5 delegate logins) — a device-identity-bound refusal, the current open blocker. Velocity note: a persisted-password-hash re-login was rejected once with -22406 ("enter the correct password") after four full logins in 30 minutes, with the identical hash derivation succeeding twice earlier in the same window — treat as server-side velocity throttling, not a derivation defect.
- Rev 24 (2026-09-28, first live probes of this implementation — two unauthenticated bag GETs + a no-client-cert courier TLS handshake, runner `:engine:runProbe`; nothing else touched): (1) C18 SETTLED — the courier demands the Albert client certificate via a fatal TLS 1.3 `certificate_required` alert after a successful handshake, on both ports (§3.1); (2) the bags are TYPED CONFIG DICTIONARIES, not URL maps — the live IDS bag carries ints/bools/floats/string-lists alongside 400+ URLs and a `bag-expiry-timestamp` (~17-day observed TTL; 403 entries live vs 405 on 2026-09-10, two new push-debounce keys in, four A/B-test keys out); the APNs bag is 65 stable entries incl. BAA-authentication flags (`APNSEnableBAAAuthenticationForDevices` etc. — C17/C20-relevant); (3) AppleTrust's embedded-root pinning validates the live chains of `init.ess.apple.com` and `courier.push.apple.com` (no root served — chain anchored at the embedded root); (4) rev-23's container-fact follow-ups: sort-version 0 and the ref-width rule are now capture/probe-consistent.
- Rev 23 (2026-09-27, writer-pass follow-up, [CF] public material + host-decoder empirical cross-check only — no restricted source): three §4 container-format gaps found by the first writer session are closed — integer signedness (1/2/4-byte forms unsigned; the 8-byte form is two's-complement signed; non-negative values take the minimal width, negative values always the 8-byte form), the object-reference width rule (minimal width whose 2^(8w) ≥ object count, so every reference 0..count−1 is representable), and the sort-version byte (written as 0).