diff --git a/demo.py b/demo.py index 577bb80..f3940cc 100644 --- a/demo.py +++ b/demo.py @@ -6,6 +6,7 @@ from subprocess import PIPE, Popen from rich.logging import RichHandler import apns +import ids.facetime import ids import imessage @@ -80,6 +81,7 @@ async def main(): CONFIG["id"] = { "key": user._id_keypair.key, "cert": user._id_keypair.cert, + "ft_cert": user._facetime_cert, } CONFIG["auth"] = { "key": user._auth_keypair.key, @@ -96,6 +98,8 @@ async def main(): with open("config.json", "w") as f: json.dump(CONFIG, f, indent=4) + await ids.facetime.provision_alias(user) + im = imessage.iMessageUser(conn, user) # Send a message to myself diff --git a/ids/__init__.py b/ids/__init__.py index 381e3ce..0ae1262 100644 --- a/ids/__init__.py +++ b/ids/__init__.py @@ -66,7 +66,7 @@ class IDSUser: self.ec_key, self.rsa_key will be set to a randomly gnenerated EC and RSA keypair if they are not already set """ - cert = identity.register( + certs = identity.register( b64encode(self.push_connection.credentials.token), self.handles, self.user_id, @@ -75,7 +75,8 @@ class IDSUser: self.encryption_identity, validation_data, ) - self._id_keypair = _helpers.KeyPair(self._auth_keypair.key, cert) + self._id_keypair = _helpers.KeyPair(self._auth_keypair.key, certs["com.apple.madrid"]) + self._facetime_cert = certs["com.apple.private.alloy.facetime.multi"] def restore_identity(self, id_keypair: _helpers.KeyPair): self._id_keypair = id_keypair @@ -104,7 +105,8 @@ class IDSUser: (rsa_key := encryption.get("rsa_key")) and (signing_key := encryption.get("ec_key")) and (cert := id.get("cert")) and - (key := id.get("key")) + (key := id.get("key")) and + (ft_cert := id.get("ft_cert")) ): self.encryption_identity = identity.IDSIdentity( encryption_key=rsa_key, @@ -113,6 +115,8 @@ class IDSUser: id_keypair = _helpers.KeyPair(key, cert) self.restore_identity(id_keypair) + + self._facetime_cert = ft_cert else: logging.info("Registering new identity...") import emulated.nac @@ -124,4 +128,3 @@ class IDSUser: async def lookup(self, uris: list[str], topic: str = "com.apple.madrid") -> Any: return await query.lookup(self.push_connection, self.current_handle, self._id_keypair, uris, topic) - diff --git a/ids/facetime.py b/ids/facetime.py new file mode 100644 index 0000000..c534972 --- /dev/null +++ b/ids/facetime.py @@ -0,0 +1,62 @@ +from ids import IDSUser +import logging +logger = logging.getLogger("ids") +import plistlib +from ids._helpers import PROTOCOL_VERSION, KeyPair, parse_key, serialize_key +from ids.signing import add_auth_signature, armour_cert, add_id_signature, add_push_signature +import requests +from base64 import b64decode, b64encode + +async def provision_alias(user: IDSUser): +# +# +# +# +# attributes +# +# allowedServices +# +# com.apple.private.alloy.facetime.multi +# +# +# expiry-epoch-seconds +# 1726418686.6028981 +# featureId +# Gondola +# +# operation +# create +# + import time + logger.debug(f"Adding new temp alias") + body = { + "attributes": { + "allowedServices": { + "com.apple.private.alloy.facetime.multi": [] + }, + # Unix timestamp in seconds, with decimal places, for 1 year from now + "expiry-epoch-seconds": time.time() + 31536000, + "featureId": "Gondola" + }, + "operation": "create" + } + body = plistlib.dumps(body) + + headers = { + "x-protocol-version": PROTOCOL_VERSION, + # "x-auth-user-id-0": user.user_id, + "x-id-self-uri": "mailto:testu3@icloud.com" + } + #add_auth_signature(headers, body, "id-register", user._auth_keypair, user._push_keypair, b64encode(user.push_connection.credentials.token), 0) + add_push_signature(headers, body, "id-provision-alias", user._push_keypair, b64encode(user.push_connection.credentials.token)) + add_id_signature(headers, body, "id-provision-alias", user._id_keypair, b64encode(user.push_connection.credentials.token)) + + r = requests.post( + "https://identity.ess.apple.com/WebObjects/TDIdentityService.woa/wa/provisionAlias", + headers=headers, + data=body, + verify=False, + ) + r = plistlib.loads(r.content) + + print(r) diff --git a/ids/identity.py b/ids/identity.py index b6c3ed4..4fba556 100644 --- a/ids/identity.py +++ b/ids/identity.py @@ -151,7 +151,28 @@ def register( "user-id": user_id, } ], - } + }, + { + "capabilities": [{"flags": 1, "name": "Invitation", "version": 1}], + "service": "com.apple.private.alloy.facetime.multi", + "sub-services": [], + "users": [ + { + "client-data": { + "public-message-identity-key": identity.encode(), + "public-message-identity-version": 2, + "supports-avless": True, + "supports-co": True, + "supports-gft-calls": True, + "supports-gft-errors": True, + "supports-modern-gft": True, + "supports-self-one-to-one-invites": True, + }, + "uris": uris, + "user-id": user_id, + } + ], + }, ], "validation-data": b64decode(validation_data), } @@ -184,5 +205,10 @@ def register( raise Exception(f"No users in response: {r}") if not "cert" in r["services"][0]["users"][0]: raise Exception(f"No cert in response: {r}") + + return { + "com.apple.madrid": armour_cert(r["services"][0]["users"][0]["cert"]), + "com.apple.private.alloy.facetime.multi": armour_cert(r["services"][1]["users"][0]["cert"]) + } - return armour_cert(r["services"][0]["users"][0]["cert"]) + #return armour_cert(r["services"][0]["users"][0]["cert"]) diff --git a/ids/signing.py b/ids/signing.py index f03ef07..5530544 100644 --- a/ids/signing.py +++ b/ids/signing.py @@ -90,11 +90,7 @@ def add_auth_signature( push_token: str, auth_number=None, ): - push_sig, push_nonce = _sign_payload(push_key.key, bag_key, "", push_token, body) - headers["x-push-sig"] = push_sig - headers["x-push-nonce"] = b64encode(push_nonce) - headers["x-push-cert"] = dearmour(push_key.cert) - headers["x-push-token"] = push_token + add_push_signature(headers, body, bag_key, push_key, push_token) auth_sig, auth_nonce = _sign_payload(auth_key.key, bag_key, "", push_token, body) auth_postfix = "-" + str(auth_number) if auth_number is not None else "" @@ -102,6 +98,18 @@ def add_auth_signature( headers["x-auth-nonce" + auth_postfix] = b64encode(auth_nonce) headers["x-auth-cert" + auth_postfix] = dearmour(auth_key.cert) +def add_push_signature( + headers: dict, + body: bytes, + bag_key: str, + push_key: KeyPair, + push_token: str +): + push_sig, push_nonce = _sign_payload(push_key.key, bag_key, "", push_token, body) + headers["x-push-sig"] = push_sig + headers["x-push-nonce"] = b64encode(push_nonce) + headers["x-push-cert"] = dearmour(push_key.cert) + headers["x-push-token"] = push_token def add_id_signature( headers: dict,