mirror of
https://github.com/fenleon/open-madrid.git
synced 2026-10-22 16:12:35 +00:00
69 lines
3.2 KiB
Markdown
69 lines
3.2 KiB
Markdown
# Using Open Madrid
|
|
|
|
Open Madrid is a set of plain JVM/Android Kotlin modules. All Apple endpoints are resolved from Apple's own bag files at runtime; nothing is hardcoded beyond the roots the TLS pinning needs.
|
|
|
|
## Getting it
|
|
|
|
No Maven repository yet — the fastest route is [JitPack](https://jitpack.io/#fenleon/open-madrid): it builds tagged commits on demand. Pick a tag, then take the modules you need:
|
|
|
|
```kotlin
|
|
repositories { maven("https://jitpack.io") }
|
|
|
|
dependencies {
|
|
implementation("com.github.fenleon.open-madrid:registration:v0.1.0")
|
|
implementation("com.github.fenleon.open-madrid:engine:v0.1.0")
|
|
}
|
|
```
|
|
|
|
Modules: `codec`, `courier`, `crypto`, `ids`, `registration` (plain JVM), `engine` (Android library, the composition root). Vendoring the source works too — every module builds standalone.
|
|
|
|
## The validation-stamp mint (`registration`)
|
|
|
|
The NAC stamp is what an Apple validation endpoint wants as proof of hardware identity. Inputs come from the three-step §1.1 exchange (spec §1.1):
|
|
|
|
```kotlin
|
|
// step 2: POST a NacRequestBlob, get session-info back
|
|
val pearKey16 = SecureRandom().generateSeed(16)
|
|
val blob = NacRequestBlob.build(certChain.last(), pearKey16, SecureRandom())
|
|
val sessionInfo = certClient.initializeValidation(blob)
|
|
|
|
// key establishment + the stamp
|
|
val established = NacMint.keyEstablishmentFull(sessionInfo, pearKey16)
|
|
val stamp = NacMint.mint(
|
|
cert79 = established.trailing.copyOfRange(0, 79),
|
|
session250 = HardwareConfiguration.from(identifiers).toBin(),
|
|
rngPad = ByteArray(0x17c - 250).also(rng::nextBytes),
|
|
rand16 = ByteArray(16).also(rng::nextBytes),
|
|
body576 = established.payload.copyOfRange(2, 578),
|
|
)
|
|
```
|
|
|
|
`identifiers` is a Mac hardware identity — the C58 "Mac-Hardware-Info" export code from a Mac carries all of it (see `MacHardwareInfoExport`).
|
|
|
|
## Anisette headers (`registration`)
|
|
|
|
```kotlin
|
|
val provider = ClearAnisetteProvider(http, stateStore, loginInfo, rng)
|
|
val provisioned = provider.ensureProvisioned() // live GSA machine provisioning
|
|
val headers = provider.anisetteValues(provisioned, System.currentTimeMillis())
|
|
```
|
|
|
|
`http` is any `IdsHttp`. **Important:** Apple's validation endpoint (`identity.ess.apple.com`) rejects every JVM TLS stack by handshake fingerprint — it only accepts rustls-shaped clients. Use a small rust transport (reqwest + rustls, two functions, exposed over UniFFI — see the `ids-bridge` in this org's imessage tool) for the two §1.1 calls. The GSA endpoints accept plain JVM TLS.
|
|
|
|
## Everything wired together (`engine`)
|
|
|
|
`Engine` composes the bag fetcher, IDS store, key pairs, HTTP and courier connections over one config:
|
|
|
|
```kotlin
|
|
val engine = Engine(Engine.Config(workingDir = File(context.filesDir, "imessage")))
|
|
engine.http // IdsHttp over the platform stack (pinned Apple roots)
|
|
engine.bagFetcher // IDS + APNs bags
|
|
engine.courier() // the APNs courier connection
|
|
```
|
|
|
|
Registration flows live in the `registration` module: `GsaLoginChain` (Apple-ID sign-in, SRP + 2FA), `GsaCpd`, `GsaDelegateLogin`, `AlbertActivation`, `IdsActivation` — the imessage tool composes them; see its `ImessageRegistration` for a worked end-to-end wiring.
|
|
|
|
## Support status
|
|
|
|
See the README and the spec's open-questions checklist. Message send/receive over the courier is not implemented yet.
|