The ids-bridge (imessage) takes its trust anchors per call from this pool —
the VEN endpoint's TLS fingerprint gate (rev 38) requires a rustls-shaped
handshake, and the JVM stacks cannot provide one. Pure visibility change.
The user's 2FA re-login re-fired the app's own IDS registration; gdb traps
showed the full mint chain ACCEPTED live (key_establishment + sign after a
successful step-2 parse, x3). Fresh-genuine-blob transport matrix: reqwest
0.11 + rustls 0.21 (+ the vendored baked Apple root DER) gets a real
session-info response on the identical XML-plist envelope; curl-OpenSSL over
h2 and h1.1, and curl with UA/Accept stripped, gets 5003/EM on the same
blobs. HTTP version and headers are irrelevant — the VEN endpoint
fingerprints the TLS ClientHello.
Kotlin blob/framing fully vindicated; the tool's :server needs a
rustls-shaped handshake (implementation decision open).
Both found by running ClearAnisetteProvider against Apple's live endpoints
(first live Kotlin provisioning run — succeeds after the fixes):
- lowercase {header, request} on the start/finish POST gets a silent HTTP 200
with an EMPTY body; {Header, Request} gets the real Response.{spim,ptm,tk}
- the GSA lookup GET 404s without X-Mme-Client-Info and only accepts the
angle-bracket form <Model> <macOS;ver;build> <com.apple.AuthKit/1 (…)>
Also records the session-26 step-2 EM A/B: the 5003/EM gate reproduces on a
FRESH genuine blob (ValidationCtx::new under gdb, replay-state eliminated)
and with the full anisette header set — anisette-on-step-2 falsified; the
app's own-flow verdict is blocked on interactive IDS re-registration.
- toBin(): the closed HardwareConfig::to_bin grammar — write_str/write_item prefixes,
model, MAC (raw 6 B), serial, UUIDs, board, build, the 00 03 0b 52 constant, then
the seven binary items in C58 export field order (serialEnc, platformUuidEnc,
rootDiskUuidEnc, ROM, romEnc, MLB ASCII, mlbEnc). Corrects the rev-32/35 label:
the raw second field is the MAC, not the ROM (multicast-bit + stub-type evidence).
- Round-trip verified byte-exact against the live 250-B session250 capture.
- ValidationCertChain: the step-1 cert data's framing (01, count, per cert
u16be type, u16be len, DER; leaf LAST = DRM Technologies A01, the OAEP key cert).
- NacMint/NacValidation: rngPad130 -> rngPad, generalized to 0x17c - session250.size.
- Live smoke result recorded: step 1 OK vs Apple, step 2 returns 5003/EM — blob
construction re-verified against the replay dumps; residue is outside the blob
(spec rev 36, NAC-NOTES session-26). 217 tests green.
Direct in-process replay of ValidationCtx::new (two byte-verified runs, no Apple
network): pearKey16 = the first RNG16 draw (= init_key input = OAEP msg[0:16]);
the post-encrypt 'xor' loops are byte-substitution tables (0x2249f9/0x224af9,
appended to PearTablesData); the pear-AES plaintext = substitute(02 ‖ u32be(1) ‖
innerKey16 ‖ zeros(10)); the encrypt_cbc iv rides an in/out 16-B block.
NacRequestBlob builds the blob with platform OAEP + HmacSHA1; 4 synthetic-vector
tests; :registration:test 213 green. Remaining live-mint gate: C56-c only.
Two session-20-frozen constants falsified on fresh live inputs:
- staging i==10 ecx2 = perm2u[st[0x6fa2]] (was frozen perm2u[0x40])
- the P region (state+0x6898, 7x256): five of seven tables are parameterized
on five body-derived byte-XOR pairs by the SSE builder at 0x17a1381-0x17a20b5
(tables 2/5 constant). The builder's instruction stream is serialized into
generated NacPTable.kt (1833 ints + 46 const vectors) and re-executed
lane-wise, byte-exact vs the emulated builder on fresh + random + s20 states.
NacSign re-ported with every body-derived region computed: u32tab/nacMasksFlags
(12-step barrett chain, full 128-bit product), mapKey cursor0, dynamic N,
base-7 0x6fd8 selectors, region6f98(state) (tsel6f98 span, 4 tables strided
0xf0), masks/flags at 0x6fe8/0x6fec, scatterMutated & ~masks, cipher RC =
state 0x25d8 words. The four former state-embedded constants (region6f98/
sel6fd8/maskC68/pTable) dropped from NacSignTablesData.
Verified: fresh live sig16 307d7151... and the full fresh 517-B stamp
byte-exact vs the live capture; session-20 stamp still byte-exact;
sig_hand.py verify 20/20. 209 registration tests green (vectors regenerated).
Spec -> rev 33. Method + dumps: registration/dev/clearadi/captures/NAC-NOTES.md
(gitignored).
The verified pure-python sign model (sig_hand/sig_init/sig_tail) is now
transcribed to Kotlin: NacSignTablesData.kt carries 102 tables as generated
base64 source (ClearAdiTablesData precedent, provenance header), NacSign.kt
runs the pipeline (u32tab → scatter → perms → keygen → phases B–E → rounds →
genBufs → threshold-cipher tail), and NacValidation.mint computes sig16
internally — the full 517-B stamp from Kotlin inputs. Pinned by 5 synthetic
LCG vectors against the python model (208 tests, 0 failed; table images
re-decoded and compared table-by-table, 32/32). Spec rev 31; README status
updated. NacMint: minimal public facade for consumers (the imessage tool
wires ClearAnisetteProvider + the mint behind its AnisetteSource/
ValidationDataSource seams).
The tail threshold cipher and pre-init region closed via empirical fit
against the 270 checkpointed rounds — pure-Python model (no emulator, no
captures) reproduces sig16 and the full 517-B stamp byte-exact (verify
20/20, independently re-verified). Spec rev 30 residue line corrected;
README: anisette chain now includes the NAC mint (Kotlin signature port
next).
Two further live provisioning runs (gdb_prov8/9) close the last three
TODO(capture) items of the cpim:
- X (the 32-byte tail group) = SHA-256(encout || spim_map_signature(
trailing[0:16]) || fixed 24-byte filler), written to the wire byte-exact;
verified against both runs.
- The signature input is the spim trailing's first 16 bytes (server
material, varies per session) — the earlier 'constant block' reading was
a stale session's value; the signature model matches the live output
exactly.
- The 12-byte plaintext tail is 12 fresh RNG bytes (same thread-RNG pool
construction as the IV).
- trailing[0:16] and the sig/digest 'nowhere on the wire' mystery resolved:
both ride transitively through X.
Kotlin: CpimShaTailSeal (the pinned production seal) wired as the provider
default; doc comments updated; one synthetic-vector test. Spec rev 29.
195 tests green.
One real OpenBubbles provisioning run under gdb (5 attempts, method + dumps in
registration/dev/clearadi/captures/WIRE-NOTES.md) pins what the previous
session left TODO(capture):
- ProvisionedData = {metadata Vec (400 B = decrypt out[4:404], len u32 BE at
out[0]), client_secret = RNG draw 2, mid = RNG draw 3, flavor u8}; the finish
gate is the decrypt output's out[408:440] == draw1 echo (not out[0:32])
- PTM = [u32 BE 4][seed 16][u32 BE payloadLen][payload][tail]; the decrypt
chain seed rides in the PTM itself (finish 17f2557 rcx = ptm[4:20], live)
- cpim = a 276-byte OUTER message [u32be(5)][iv 16][u32be(160)][encout 160]
[X 32][u32be(52)][u32be(4)][Y 52] over the 160-byte plaintext
[len][encsec(rng2)][len][rng1][blob=mid][arg9=-2 BE][unix-seconds BE]
[u32be(1)][12 B]; arg9 = -2 live-confirmed
- the encrypt IV is 16 plain RNG bytes (the 'IV from Spim trailing fields'
reading was the thread-RNG pool, not the Spim)
- the spim carries 99 trailing bytes (the <=23-B cap was a probe artifact) and
trailing[16:76] relays verbatim as the wire tail's Y group
- spim_map_signature args are (rdi=in, rsi=out) and neither its output nor the
SHA-256(encout) digest appears on the wire — the 'two 256-bit tail fields'
reading is falsified
- decryptPTM's math model round-trips the live PTM byte-exact (gate echo found
at out[408])
Remaining TODO(capture): the X 32-byte group's source, the 12-byte plaintext
tail, the spim trailing's first 16 bytes. Kotlin ClearAdiFraming /
ClearAnisetteProvider restructured to the live shapes; README updated.
194 tests green (:registration:test).
Engine modules: :codec (binary+XML plists), :courier (APNs push client),
:ids (identity lookup), :crypto (the encryption envelope), :registration
(GSA login, albert activation, renewal), :engine (integration + live
drift probes). 344 offline tests; capture-fixture known-answer vectors
for the parsers; live unauthenticated probes validated against Apple.
The bundled FairPlay identity is a synthetic placeholder — Apple-derived
material is user-staged, never shipped. Apache-2.0.