Messages is a protocol on top of APNS used for delivering both iMessage and SMS forwarded messages.
Encryption Format
Generate a random 11 byte HMAC. Sign with SHA256 hash the following structure:
- Body
- [0x2]
- Sender identity hash
- Target identity hash
Construct an AES key with the 11 byte HMAC key, and the first 5 bytes of the signature. Encrypt the body with AES 128 CTR, with a nonce that is 15 0 bytes followed by a 1.
Sign with the recipient's public encryption key. PKCS1 OAEP, OAEP MD and MGF MD are both SHA1. Encrypt the key and the first 100 bytes (or less) of the AES body. Concat the rest of the AES body onto the RSA ciphertext.
Sign the final ciphertext with your signing key, SHA1 digest. Final payload:
- [0x2]
- 16-bit BE payload length
- Payload
- 8-bit signature length
- Signature
Client
APS setup
- Set state 1
- Filter
com.apple.madrid, com.apple.private.alloy.sms
- Wait for NoStorage msg or timeout if timeout then:
- Send flush cache msg (c: 160, e: ns since epoch)
Send format
- Cache keys for target
- Create payload bundles for each token, remembering not to send to yourself. Encrypt the payload, and construct this plist. Keep track of payload length.
Payload bundle plist
- tP: target participant handle
- D: send delivered
- sT: session token
- P: payload (enc)
- t: token
Generate a random message id (32 bits). Send payloads in batches, not exceeding 10k bytes each:
- fcn: Batch #
- c: Command
- E: pair if payload, otherwise absent
- ua: version UA
- v:
8
- i: 32 bit message id
- U: message UUID (binary)
- dtl: list of bundled payloads
- sP: sender handle
- eX: message eX
- nr: message no response
If NR is not true, wait for 15 seconds (or 50%) of c: 255 messages with U: uuid. Read s. If s is 0 or 5008 ignore, otherwise error the send. If 5032, re-query affected participant. Retry sending to affected participants.
Enable SMS reg
Command 145:
- wc: false,
- ar: true,
Send with NR, but if recv without NR send back a read message confirming it.
Deactivate:
- ue: true,
Cache invalidation
Command 130 Invalidate sender cache on target's handle.
- sP: sender
- tP: target
If sent by target handle, query private device info and check if there are new handles. Reregister if so. In any case, tell all recent chats to invalidate their cache (c130)
Text message
- t?: Text
- x?: Xml
- ix?: live xml (photos)
- p: Vec<String>, (participants)
- r?: After guid (string)
- gid?: Sender guid (string)
- pv: u64
0
- gv: String
8
- v: String
1
- iid?: effect
- n?: conversation name
- tg?: reply
- ia-0?: inline attachment 0
- ia-1?: inline attachment 1
Phone gateways
Make an HTTP GET request to https://itunes.apple.com/WebObjects/MZStore.woa/wa/com.apple.jingle.appserver.client.MZITunesClientCheck/version?languageCode=en
Deserialize these two fields:
- MobileDeviceCarriersByMccMnc: MobileCarrier(BundleName?: string, MVNOs?: MobileCarrier[])
- MobileDeviceCarrierBundlesByProductVersion
Find your MccMnc inside MobileDeviceCarriersByMccMnc. Get bundles for that carrier by either using the bundle name, or using a collection of the bundles of all the MVNOs.
Get each bundle from MobileDeviceCarrierBundlesByProductVersion, and parse it into a dictionary of objects of type {BundleURL?: string}. Get the latest version (key), then fetch that URL. Unzip that archive, then find one that starts with Payload/ and ends with /carrier.plist. Read that, then read PhoneNumberRegistrationGatewayAddress.