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.