Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Log in or create an account to edit The Apple Wiki.

Identity Services

From The Apple Wiki
(Redirected from IDS)

Identity Services ("IDS") is a term that can be used to describe a protocol, key server, and encryption mechanism used by FaceTime and iMessage to facilitate end-to-end encryption, or it can refer to the local daemon ("identityservicesd") that implements the aforementioned protocol. Note that Identity Services can also refer to a system used on macOS for local user authentication.[1]

Bag

https://init.ess.apple.com/WebObjects/VCInit.woa/wa/getBag?ix=3

Authentication

Generate a 2048 bit RSA key. Create a CSR with this key:

  • Common Name: Uppercase HEX Sha1 hash of User ID
  • Version: 0
  • Public key and signature from generated key.

Post to desired endpoint from bag with the non-binary gzipped plist:

  • authentication-data: Service-Specific auth data
  • csr: (data) CSR (der)
  • realm-user-id: User ID

With headers:

  • GZip send/recv
  • user-agent: com.apple.invitation-registration ...
  • x-protocol-version: Protocol version

Responds with:

  • status: Should be 0
  • cert: (data) DER encoded auth cert

Apple ID

Endpoint: id-authenticate-ds-id
Auth Data:

  • auth-token: token from loginDelegates

Phone number

Endpoint: id-authenticate-phone-number
User ID: P:{phone number}
Auth Data:

  • push-token: (data) push token
  • sigs: (array of data) Phone Signatures

Request Signing

Body is signed, use gzipped if gzipped.

Nonce format

[01(http)/00(apns auth)] [time in ms rounded to sec, 64 bit BE] [8 random bytes]

Payload format

Nonce + Data Fields (BE 32-bit length, then field)
HTTP fields:

  • Bag Key
  • Query String
  • Payload
  • Push token

Signature format

PKCS1 SHA1 RSA signature of payload, prefixed with two 0x1 bytes.

Headers format

All base64 encoded

  • x-item-nonce
  • x-item-sig
  • x-item-cert

Auth Requests

Get Handles

Send an HTTP GET to id-get-handles
Headers:

  • x-push-token: Push token
  • Sign with push and auth
  • x-protocol-version: Protocol version
  • x-auth-user-id: User ID

Sample response:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>handles</key>
    <array>
      <dict>
        <key>aliases</key>
        <dict></dict>
        <key>is-user-visible</key>
        <true/>
        <key>uri</key>
        <string>mailto:user_test2@icloud.com</string>
        <key>status</key>
        <integer>5051</integer>
      </dict>
    </array>
    <key>self-handle</key>
    <dict>
      <key>uri</key>
      <string>urn:ds:20994360971</string>
    </dict>
    <key>status</key>
    <integer>0</integer>
  </dict>
</plist>

Registration

Send an HTTP post to id-register with headers:

  • x-push-token: Push token
  • Sign with push and auth, auth prefixed with N for each user
  • x-protocol-version: Protocol version
  • x-auth-user-id-N: User ID for user N
  • user-agent: com.apple.invitation-registration ...
  • content-type: application/x-apple-plist
  • Gzip headers

HTTP body plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>device-name</key>
    <string>(Device name)</string>
    <key>hardware-version</key>
    <string>(SMBIOS version}</string>
    <key>language</key>
    <string>en-US</string>
    <key>os-version</key>
    <string>macOS,(version eg. 13.2.1),(build num eg. 22D68)</string>
    <key>private-device-data</key>
    <dict>
      (private device data)
    </dict>
    <key>retry-count</key>
    <integer>0</integer>
    <key>services</key>
    <array>
      <dict>
        <key>capabilities</key>
        <array>
          <dict>
            <key>flags</key>
            <integer>17</integer>
            <key>name</key>
            <string>Messenger</string>
            <key>version</key>
            <integer>1</integer>
          </dict>
        </array>
        <key>service</key>
        <string>com.apple.madrid</string>
        <key>sub-services</key>
        <array>
          <string>com.apple.private.alloy.sms</string>
          <string>com.apple.private.alloy.gelato</string>
          <string>com.apple.private.alloy.biz</string>
          <string>com.apple.private.alloy.gamecenter.imessage</string>
        </array>
        <key>users</key>
        <array>
          <dict>
            <key>client-data</key>
            <dict>
              <key>public-message-identity-key</key>
              <data>
                (encoded identity)
              </data>
              (...extras)
            </dict>
            <key>uris</key>
            <array>
              <dict>
                <key>uri</key>
                <string>mailto:example@test.com</string>
              </dict>
              <dict>
                <key>uri</key>
                <string>mailto:sidestorer@test.com</string>
              </dict>
            </array>
            <key>user-id</key>
            <string>(user_id)</string>
            (if phone)
            <key>tag</key>
            <string>SIM</string>
            (/if)
          </dict>
        </array>
      </dict>
    </array>
    <key>software-version</key>
    <string>(build num)</string>
    <key>validation-data</key>
    <data>
      (validation data)
    </data>
  </dict>
</plist>

Response

Plist format:

  • status: should be 0
  • services: List of services, first is imsg,.then has users

For each user:

  • status: if 6009, get alert key of struct:
title: String,
body: String,
action?: {
    url: String,
    button: String,
},

Otherwise,

  • cert: DER-encoded ID cert
  • uris: List of URIs, each with a "status" field (check all 0)
  • user-id: user id of this user for matching


Identity Key Format

ASN.1 SEQUENCE with two byte arrays. First byte array is EC key. It's padded with 0x00 0x41. Starts with 0x4, then contains X and Y EC coordinates padded to 32 bytes. Second one is an x509 public key, padded with 0x00 0xAC.

Encryption key is 1280 bit RSA. EC key is type X9_62_PRIME256V1.

Hash

[Padded EC + Padded RSA] sha256

Pair-EC

Compact EC key format

Generation

Generate a P256 EC Key such that y <= P - y. Just keep generating if it doesn't work :) (this is official Apple strategy)

Encoding

Just encode `X`. We know which side of the curve it'll be on so Y doesn't matter.

Decoding

Pad the `X` with 0x3. Decode the compressed EC point. If y >= P - y set Y point to P - y.

Protobuf structures

message PreKeyData {
    bytes key = 1;
    bytes signature = 2;
    double timestamp = 3;
}
  • key: encoded prekey
  • signature: Signature (see device data)
  • timestamp: Signed timestamp (see device data)
message KtLoggableData {
    message NgmPublicIdentity {
	    optional bytes publicKey = 1;
    }
    optional NgmPublicIdentity deviceIdentity = 1;
    optional uint32 ngmVersion = 2;
    optional uint32 ktVersion = 3;
}
message InnerMessage {
    bytes message = 1;
    uint32 counter = 2;
    bytes ktGossipData = 3;
    bytes debugInfo = 99;
}
message OuterMessage {
    bytes payload = 1;
    bytes key = 2;
    bytes signature = 3;
    bytes validator = 99;
}

Device data

Generate two compact keys, a device key and a pre key. Registration data:

  • ec-version: 1
  • public-message-identity-ngm-version: 13
  • public-message-ngm-device-prekey-data-key: PreKeyData protobuf
  • kt-version: 5
Signature format
  • Generate a byte string: "NGMPrekeySignature${prekeybytes}${secondssinceepochasfloat64LE}"
  • Sha256 that.
  • EC Sign that with your device key.
  • r is lower 32 bytes, s is upper 32 bytes
  • Timestamp must match PreKeyData struct

KT Loggable data

  • kt-loggable-data (adjacent to client-data, lookups may place it in "ngm-public-identity" in client-data): KtLoggableData protobuf

Counter format

Hash my device public key, their pre key, and their device public key. Increment a counter on message send

Key validator format

7 Byte message

  • First 2 bytes of sender device key
  • First 2 bytes of receiver device key
  • First 2 bytes of receiver pre key
  • Something (don't check) 0xc (sample)


Encryption

Create an InnerMessage with the message and counter. Pad the protobuf-encoded message with random data such that the length is a multiple of 16. Append the amount of added bytes after the padding as a 32-bit unsigned little endian integer.

Make sure you check their prekey for validity first.

Create ephemeral compact EC key. ECDH the ephemeral key with their pre key to get a shared secret. Perform sha256 HKDF on the shared secret, with the salt "LastPawn-MessageKeys", with null info. First 32 bytes are an AES key, next 16 bytes are the IV.

AES CTR 64-bit counter (NONSTANDARD) with the key and IV, encrypt the padded message.

Signature data (concat): sharedSecret + targetPreKey + msgEphermeralKey + targetDeviceKey + ciphertext Sha256 the signature data. EC sign with the device key. R bottom 32 bits, s top 32 bits.

Build OuterMessage.

Query

Make an HTTP request over APNs to id-query
Body: gzipped (no header) text plist:

  • uris: List of string to query

Headers:

  • x-id-self-uri: Self handle
  • x-protocol-version: Protocol version
  • user-agent: com.apple.madrid-lookup ...
  • x-push-token: push token
  • sign with ID cert

Response: Plist:

  • status: should be 0
  • results?: Map of URI to response

Response:

  • identities: List of identities

Identity:

  • client-data: client data
  • push-token: push token (bin)
  • session-token: session token (bin)
  • session-token-expires-seconds
  • session-token-refresh-seconds

HTTP over APNs

  • cT: content-type
  • U: message id
  • c: 96
  • u: URL (from bag)
  • h: headers list
  • v: 2
  • b: gzipped body (mtime 0)

Response

  • U: message ID
  • b: gzipped response