mirror of
https://github.com/Sneed-Group/pypush-plus-plus
synced 2025-01-09 17:33:47 +00:00
formatting
This commit is contained in:
parent
7594eabf7d
commit
290164af8e
4 changed files with 197 additions and 126 deletions
|
@ -1,17 +1,26 @@
|
||||||
import uuid
|
|
||||||
import plistlib
|
|
||||||
from . import gsa
|
|
||||||
import logging
|
|
||||||
import requests
|
|
||||||
import base64
|
import base64
|
||||||
|
import logging
|
||||||
|
import plistlib
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
from emulated import nac
|
from emulated import nac
|
||||||
|
|
||||||
|
from . import gsa
|
||||||
|
|
||||||
logger = logging.getLogger("icloud")
|
logger = logging.getLogger("icloud")
|
||||||
|
|
||||||
USER_AGENT = "com.apple.iCloudHelper/282 CFNetwork/1408.0.4 Darwin/22.5.0"
|
USER_AGENT = "com.apple.iCloudHelper/282 CFNetwork/1408.0.4 Darwin/22.5.0"
|
||||||
|
|
||||||
def login(username: str, password: str, delegates: set[str] = ["com.apple.private.ids"], grandslam: bool = True, anisette: str | bool = False):
|
|
||||||
|
def login(
|
||||||
|
username: str,
|
||||||
|
password: str,
|
||||||
|
delegates: set[str] = ["com.apple.private.ids"],
|
||||||
|
grandslam: bool = True,
|
||||||
|
anisette: str | bool = False,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Logs into Apple services listed in `delegates` and returns a dictionary of responses.
|
Logs into Apple services listed in `delegates` and returns a dictionary of responses.
|
||||||
Commonly used delegates are:
|
Commonly used delegates are:
|
||||||
|
@ -50,11 +59,12 @@ def login(username: str, password: str, delegates: set[str] = ["com.apple.privat
|
||||||
"X-Apple-ADSID": adsid,
|
"X-Apple-ADSID": adsid,
|
||||||
"X-Mme-Nas-Qualify": base64.b64encode(v).decode(),
|
"X-Mme-Nas-Qualify": base64.b64encode(v).decode(),
|
||||||
"User-Agent": USER_AGENT,
|
"User-Agent": USER_AGENT,
|
||||||
"X-Mme-Client-Info": gsa.build_client(emulated_app="accountsd") # Otherwise we get MOBILEME_TERMS_OF_SERVICE_UPDATE on some accounts
|
"X-Mme-Client-Info": gsa.build_client(
|
||||||
|
emulated_app="accountsd"
|
||||||
|
), # Otherwise we get MOBILEME_TERMS_OF_SERVICE_UPDATE on some accounts
|
||||||
}
|
}
|
||||||
headers.update(gsa.generate_anisette_headers())
|
headers.update(gsa.generate_anisette_headers())
|
||||||
|
|
||||||
|
|
||||||
logger.debug("Making login request")
|
logger.debug("Making login request")
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
"https://setup.icloud.com/setup/prefpane/login",
|
"https://setup.icloud.com/setup/prefpane/login",
|
||||||
|
|
|
@ -16,7 +16,7 @@ class ULEB128:
|
||||||
assert i >= 0
|
assert i >= 0
|
||||||
r = []
|
r = []
|
||||||
while True:
|
while True:
|
||||||
byte = i & 0x7f
|
byte = i & 0x7F
|
||||||
i = i >> 7
|
i = i >> 7
|
||||||
if i == 0:
|
if i == 0:
|
||||||
r.append(byte)
|
r.append(byte)
|
||||||
|
@ -27,7 +27,7 @@ class ULEB128:
|
||||||
def decode(b: bytearray) -> int:
|
def decode(b: bytearray) -> int:
|
||||||
r = 0
|
r = 0
|
||||||
for i, e in enumerate(b):
|
for i, e in enumerate(b):
|
||||||
r = r + ((e & 0x7f) << (i * 7))
|
r = r + ((e & 0x7F) << (i * 7))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -46,7 +46,7 @@ class ILEB128:
|
||||||
def encode(i: int) -> bytearray:
|
def encode(i: int) -> bytearray:
|
||||||
r = []
|
r = []
|
||||||
while True:
|
while True:
|
||||||
byte = i & 0x7f
|
byte = i & 0x7F
|
||||||
i = i >> 7
|
i = i >> 7
|
||||||
if (i == 0 and byte & 0x40 == 0) or (i == -1 and byte & 0x40 != 0):
|
if (i == 0 and byte & 0x40 == 0) or (i == -1 and byte & 0x40 != 0):
|
||||||
r.append(byte)
|
r.append(byte)
|
||||||
|
@ -57,9 +57,9 @@ class ILEB128:
|
||||||
def decode(b: bytearray) -> int:
|
def decode(b: bytearray) -> int:
|
||||||
r = 0
|
r = 0
|
||||||
for i, e in enumerate(b):
|
for i, e in enumerate(b):
|
||||||
r = r + ((e & 0x7f) << (i * 7))
|
r = r + ((e & 0x7F) << (i * 7))
|
||||||
if e & 0x40 != 0:
|
if e & 0x40 != 0:
|
||||||
r |= - (1 << (i * 7) + 7)
|
r |= -(1 << (i * 7) + 7)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -1,22 +1,28 @@
|
||||||
from typing import Literal
|
|
||||||
from . import cloudkit_pb2, gsa, _utils
|
|
||||||
import uuid
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import typing
|
|
||||||
import random
|
|
||||||
import requests
|
|
||||||
import logging
|
import logging
|
||||||
|
import random
|
||||||
|
import typing
|
||||||
|
import uuid
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from . import _utils, cloudkit_pb2, gsa
|
||||||
|
|
||||||
logger = logging.getLogger("cloudkit")
|
logger = logging.getLogger("cloudkit")
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class Record:
|
class Record:
|
||||||
name: uuid.UUID
|
name: uuid.UUID
|
||||||
type: str
|
type: str
|
||||||
fields: dict[str, typing.Any]
|
fields: dict[str, typing.Any]
|
||||||
|
|
||||||
|
|
||||||
class CloudKit:
|
class CloudKit:
|
||||||
def __init__(self, dsid: str, cloudkit_token: str, mme_token: str, sandbox: bool = False):
|
def __init__(
|
||||||
|
self, dsid: str, cloudkit_token: str, mme_token: str, sandbox: bool = False
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Represents a CloudKit user.
|
Represents a CloudKit user.
|
||||||
`dsid`: The user's DSID.
|
`dsid`: The user's DSID.
|
||||||
|
@ -29,14 +35,24 @@ class CloudKit:
|
||||||
self.mme_token = mme_token
|
self.mme_token = mme_token
|
||||||
self.sandbox = sandbox
|
self.sandbox = sandbox
|
||||||
|
|
||||||
def container(self, container: str, scope: Literal["PUBLIC"] | Literal["PRIVATE"] | Literal["SHARED"] = "PUBLIC") -> "CloudKitContainer":
|
def container(
|
||||||
|
self,
|
||||||
|
container: str,
|
||||||
|
scope: Literal["PUBLIC"] | Literal["PRIVATE"] | Literal["SHARED"] = "PUBLIC",
|
||||||
|
) -> "CloudKitContainer":
|
||||||
"""
|
"""
|
||||||
Convenience method for creating a CloudKitContainer object.
|
Convenience method for creating a CloudKitContainer object.
|
||||||
"""
|
"""
|
||||||
return CloudKitContainer(container, self, scope)
|
return CloudKitContainer(container, self, scope)
|
||||||
|
|
||||||
|
|
||||||
class CloudKitContainer:
|
class CloudKitContainer:
|
||||||
def __init__(self, container: str, user: CloudKit, scope: Literal["PUBLIC"] | Literal["PRIVATE"] | Literal["SHARED"] = "PUBLIC"):
|
def __init__(
|
||||||
|
self,
|
||||||
|
container: str,
|
||||||
|
user: CloudKit,
|
||||||
|
scope: Literal["PUBLIC"] | Literal["PRIVATE"] | Literal["SHARED"] = "PUBLIC",
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Represents a CloudKit container.
|
Represents a CloudKit container.
|
||||||
container: The CloudKit container ID. (e.g. "iCloud.dev.jjtech.experiments.cktest")
|
container: The CloudKit container ID. (e.g. "iCloud.dev.jjtech.experiments.cktest")
|
||||||
|
@ -51,24 +67,32 @@ class CloudKitContainer:
|
||||||
def _fetch_user_id(self):
|
def _fetch_user_id(self):
|
||||||
headers = {
|
headers = {
|
||||||
"x-cloudkit-containerid": self.container,
|
"x-cloudkit-containerid": self.container,
|
||||||
"x-cloudkit-bundleid": ".".join(self.container.split(".")[1:]), # Remove the "iCloud." prefix
|
"x-cloudkit-bundleid": ".".join(
|
||||||
|
self.container.split(".")[1:]
|
||||||
|
), # Remove the "iCloud." prefix
|
||||||
"x-cloudkit-databasescope": self.scope,
|
"x-cloudkit-databasescope": self.scope,
|
||||||
"x-cloudkit-environment": "Sandbox" if self.user.sandbox else "Production",
|
"x-cloudkit-environment": "Sandbox" if self.user.sandbox else "Production",
|
||||||
|
|
||||||
"accept": "application/x-protobuf",
|
"accept": "application/x-protobuf",
|
||||||
|
|
||||||
"x-apple-operation-id": random.randbytes(8).hex(),
|
"x-apple-operation-id": random.randbytes(8).hex(),
|
||||||
"x-apple-request-uuid": str(uuid.uuid4()).upper()
|
"x-apple-request-uuid": str(uuid.uuid4()).upper(),
|
||||||
}
|
}
|
||||||
|
|
||||||
headers.update(gsa.generate_anisette_headers())
|
headers.update(gsa.generate_anisette_headers())
|
||||||
|
|
||||||
r = requests.post("https://gateway.icloud.com/setup/setup/ck/v1/ckAppInit", params={"container": self.container}, headers=headers, auth=(self.user.dsid, self.user.mme_token), verify=False)
|
r = requests.post(
|
||||||
|
"https://gateway.icloud.com/setup/setup/ck/v1/ckAppInit",
|
||||||
|
params={"container": self.container},
|
||||||
|
headers=headers,
|
||||||
|
auth=(self.user.dsid, self.user.mme_token),
|
||||||
|
verify=False,
|
||||||
|
)
|
||||||
|
|
||||||
logger.debug("Got app init response: ", r.content)
|
logger.debug("Got app init response: ", r.content)
|
||||||
return r.json()["cloudKitUserId"]
|
return r.json()["cloudKitUserId"]
|
||||||
|
|
||||||
def save_record(self, record: Record, zone: str = "_defaultZone", owner: str = "_defaultOwner") -> None:
|
def save_record(
|
||||||
|
self, record: Record, zone: str = "_defaultZone", owner: str = "_defaultOwner"
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Saves a record to the container.
|
Saves a record to the container.
|
||||||
"""
|
"""
|
||||||
|
@ -78,34 +102,33 @@ class CloudKitContainer:
|
||||||
"x-cloudkit-authtoken": self.user.cloudkit_token,
|
"x-cloudkit-authtoken": self.user.cloudkit_token,
|
||||||
"x-cloudkit-userid": self.user_id,
|
"x-cloudkit-userid": self.user_id,
|
||||||
"x-cloudkit-containerid": self.container,
|
"x-cloudkit-containerid": self.container,
|
||||||
"x-cloudkit-bundleid": ".".join(self.container.split(".")[1:]), # Remove the "iCloud." prefix
|
"x-cloudkit-bundleid": ".".join(
|
||||||
|
self.container.split(".")[1:]
|
||||||
|
), # Remove the "iCloud." prefix
|
||||||
"x-cloudkit-databasescope": self.scope,
|
"x-cloudkit-databasescope": self.scope,
|
||||||
"x-cloudkit-environment": "Sandbox" if self.user.sandbox else "Production",
|
"x-cloudkit-environment": "Sandbox" if self.user.sandbox else "Production",
|
||||||
|
|
||||||
"accept": "application/x-protobuf",
|
"accept": "application/x-protobuf",
|
||||||
"content-type": 'application/x-protobuf; desc="https://gateway.icloud.com:443/static/protobuf/CloudDB/CloudDBClient.desc"; messageType=RequestOperation; delimited=true',
|
"content-type": 'application/x-protobuf; desc="https://gateway.icloud.com:443/static/protobuf/CloudDB/CloudDBClient.desc"; messageType=RequestOperation; delimited=true',
|
||||||
|
|
||||||
"x-apple-operation-id": random.randbytes(8).hex(),
|
"x-apple-operation-id": random.randbytes(8).hex(),
|
||||||
"x-apple-request-uuid": str(uuid.uuid4()).upper(),
|
"x-apple-request-uuid": str(uuid.uuid4()).upper(),
|
||||||
|
"user-agent": "CloudKit/2060.11 (22F82)",
|
||||||
"user-agent": "CloudKit/2060.11 (22F82)"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
headers.update(gsa.generate_anisette_headers())
|
headers.update(gsa.generate_anisette_headers())
|
||||||
|
|
||||||
body = _build_record_save_request(record, self.container, self.user.sandbox, self.scope, zone, owner)
|
body = _build_record_save_request(
|
||||||
|
record, self.container, self.user.sandbox, self.scope, zone, owner
|
||||||
|
)
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
"https://gateway.icloud.com/ckdatabase/api/client/record/save",
|
"https://gateway.icloud.com/ckdatabase/api/client/record/save",
|
||||||
headers=headers,
|
headers=headers,
|
||||||
data=body,
|
data=body,
|
||||||
verify=False
|
verify=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
print(r.content)
|
print(r.content)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _build_record_save_request(
|
def _build_record_save_request(
|
||||||
record: Record,
|
record: Record,
|
||||||
container: str,
|
container: str,
|
||||||
|
@ -114,46 +137,68 @@ def _build_record_save_request(
|
||||||
zone: str = "_defaultZone",
|
zone: str = "_defaultZone",
|
||||||
owner: str = "_defaultOwner",
|
owner: str = "_defaultOwner",
|
||||||
):
|
):
|
||||||
hardware_id = uuid.uuid4() # Generate a new hardware ID for each request?
|
hardware_id = uuid.uuid4() # Generate a new hardware ID for each request?
|
||||||
operation_uuid = uuid.uuid4() # Generate a new operation UUID for each request?
|
operation_uuid = uuid.uuid4() # Generate a new operation UUID for each request?
|
||||||
record_id = uuid.uuid4() # Generate a new record ID for each request?
|
record_id = uuid.uuid4() # Generate a new record ID for each request?
|
||||||
|
|
||||||
request = cloudkit_pb2.RequestOperation()
|
request = cloudkit_pb2.RequestOperation()
|
||||||
request.header.applicationContainer = container
|
request.header.applicationContainer = container
|
||||||
request.header.applicationContainerEnvironment = cloudkit_pb2.RequestOperation.Header.ContainerEnvironment.SANDBOX if sandbox else cloudkit_pb2.RequestOperation.Header.ContainerEnvironment.PRODUCTION
|
request.header.applicationContainerEnvironment = (
|
||||||
|
cloudkit_pb2.RequestOperation.Header.ContainerEnvironment.SANDBOX
|
||||||
|
if sandbox
|
||||||
|
else cloudkit_pb2.RequestOperation.Header.ContainerEnvironment.PRODUCTION
|
||||||
|
)
|
||||||
|
|
||||||
request.header.deviceHardwareID = str(hardware_id).upper()
|
request.header.deviceHardwareID = str(hardware_id).upper()
|
||||||
|
|
||||||
if database == "PUBLIC":
|
if database == "PUBLIC":
|
||||||
request.header.targetDatabase = cloudkit_pb2.RequestOperation.Header.Database.PUBLIC_DB
|
request.header.targetDatabase = (
|
||||||
|
cloudkit_pb2.RequestOperation.Header.Database.PUBLIC_DB
|
||||||
|
)
|
||||||
elif database == "PRIVATE":
|
elif database == "PRIVATE":
|
||||||
request.header.targetDatabase = cloudkit_pb2.RequestOperation.Header.Database.PRIVATE_DB
|
request.header.targetDatabase = (
|
||||||
|
cloudkit_pb2.RequestOperation.Header.Database.PRIVATE_DB
|
||||||
|
)
|
||||||
elif database == "SHARED":
|
elif database == "SHARED":
|
||||||
request.header.targetDatabase = cloudkit_pb2.RequestOperation.Header.Database.SHARED_DB
|
request.header.targetDatabase = (
|
||||||
|
cloudkit_pb2.RequestOperation.Header.Database.SHARED_DB
|
||||||
request.header.isolationLevel = cloudkit_pb2.RequestOperation.Header.IsolationLevel.ZONE
|
)
|
||||||
|
|
||||||
|
request.header.isolationLevel = (
|
||||||
|
cloudkit_pb2.RequestOperation.Header.IsolationLevel.ZONE
|
||||||
|
)
|
||||||
|
|
||||||
request.request.operationUUID = str(operation_uuid).upper()
|
request.request.operationUUID = str(operation_uuid).upper()
|
||||||
request.request.type = cloudkit_pb2.Operation.Type.RECORD_SAVE_TYPE
|
request.request.type = cloudkit_pb2.Operation.Type.RECORD_SAVE_TYPE
|
||||||
request.request.last = True
|
request.request.last = True
|
||||||
|
|
||||||
|
request.recordSaveRequest.record.recordIdentifier.value.name = str(
|
||||||
request.recordSaveRequest.record.recordIdentifier.value.name = str(record_id).upper()
|
record_id
|
||||||
request.recordSaveRequest.record.recordIdentifier.value.type = cloudkit_pb2.Identifier.Type.RECORD
|
).upper()
|
||||||
|
request.recordSaveRequest.record.recordIdentifier.value.type = (
|
||||||
|
cloudkit_pb2.Identifier.Type.RECORD
|
||||||
|
)
|
||||||
|
|
||||||
request.recordSaveRequest.record.recordIdentifier.zoneIdentifier.value.name = zone
|
request.recordSaveRequest.record.recordIdentifier.zoneIdentifier.value.name = zone
|
||||||
request.recordSaveRequest.record.recordIdentifier.zoneIdentifier.value.type = cloudkit_pb2.Identifier.Type.RECORD_ZONE
|
request.recordSaveRequest.record.recordIdentifier.zoneIdentifier.value.type = (
|
||||||
|
cloudkit_pb2.Identifier.Type.RECORD_ZONE
|
||||||
|
)
|
||||||
|
|
||||||
request.recordSaveRequest.record.recordIdentifier.zoneIdentifier.ownerIdentifier.name = owner
|
request.recordSaveRequest.record.recordIdentifier.zoneIdentifier.ownerIdentifier.name = (
|
||||||
request.recordSaveRequest.record.recordIdentifier.zoneIdentifier.ownerIdentifier.type = cloudkit_pb2.Identifier.Type.USER
|
owner
|
||||||
|
)
|
||||||
|
request.recordSaveRequest.record.recordIdentifier.zoneIdentifier.ownerIdentifier.type = (
|
||||||
|
cloudkit_pb2.Identifier.Type.USER
|
||||||
|
)
|
||||||
|
|
||||||
request.recordSaveRequest.record.type.name = record.type
|
request.recordSaveRequest.record.type.name = record.type
|
||||||
|
|
||||||
for key, value in record.fields.items():
|
for key, value in record.fields.items():
|
||||||
request.recordSaveRequest.record.recordField.append(cloudkit_pb2.Record.Field())
|
request.recordSaveRequest.record.recordField.append(cloudkit_pb2.Record.Field())
|
||||||
request.recordSaveRequest.record.recordField[-1].identifier.name = key
|
request.recordSaveRequest.record.recordField[-1].identifier.name = key
|
||||||
request.recordSaveRequest.record.recordField[-1].value.type = cloudkit_pb2.Record.Field.Value.Type.STRING_TYPE
|
request.recordSaveRequest.record.recordField[
|
||||||
|
-1
|
||||||
|
].value.type = cloudkit_pb2.Record.Field.Value.Type.STRING_TYPE
|
||||||
request.recordSaveRequest.record.recordField[-1].value.stringValue = value
|
request.recordSaveRequest.record.recordField[-1].value.stringValue = value
|
||||||
|
|
||||||
len_bytes = _utils.ULEB128.encode(len(request.SerializeToString()))
|
len_bytes = _utils.ULEB128.encode(len(request.SerializeToString()))
|
||||||
|
|
140
icloud/gsa.py
140
icloud/gsa.py
|
@ -5,6 +5,7 @@ import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
import json
|
import json
|
||||||
import locale
|
import locale
|
||||||
|
import logging
|
||||||
import plistlib as plist
|
import plistlib as plist
|
||||||
import uuid
|
import uuid
|
||||||
from base64 import b64decode, b64encode
|
from base64 import b64decode, b64encode
|
||||||
|
@ -17,19 +18,18 @@ import srp._pysrp as srp
|
||||||
from cryptography.hazmat.primitives import padding
|
from cryptography.hazmat.primitives import padding
|
||||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
|
||||||
import logging
|
|
||||||
logger = logging.getLogger("gsa")
|
logger = logging.getLogger("gsa")
|
||||||
|
|
||||||
|
|
||||||
# Server to use for anisette generation
|
# Server to use for anisette generation
|
||||||
ANISETTE = False # Use local generation with AOSKit (macOS only)
|
ANISETTE = False # Use local generation with AOSKit (macOS only)
|
||||||
# ANISETTE = "https://sign.rheaa.xyz/"
|
# ANISETTE = "https://sign.rheaa.xyz/"
|
||||||
# ANISETTE = 'http://45.132.246.138:6969/'
|
# ANISETTE = 'http://45.132.246.138:6969/'
|
||||||
#ANISETTE = "https://ani.sidestore.io/"
|
# ANISETTE = "https://ani.sidestore.io/"
|
||||||
# ANISETTE = 'https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx'
|
# ANISETTE = 'https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx'
|
||||||
# ANISETTE = "http://jkcoxson.com:2052/"
|
# ANISETTE = "http://jkcoxson.com:2052/"
|
||||||
|
|
||||||
#USER_AGENT = "com.apple.iCloudHelper/282 CFNetwork/1408.0.4 Darwin/22.5.0"
|
# USER_AGENT = "com.apple.iCloudHelper/282 CFNetwork/1408.0.4 Darwin/22.5.0"
|
||||||
USER_AGENT = "akd/1.0 CFNetwork/978.0.7 Darwin/18.7.0"
|
USER_AGENT = "akd/1.0 CFNetwork/978.0.7 Darwin/18.7.0"
|
||||||
|
|
||||||
# Created here so that it is consistent
|
# Created here so that it is consistent
|
||||||
|
@ -45,52 +45,56 @@ import urllib3
|
||||||
|
|
||||||
urllib3.disable_warnings()
|
urllib3.disable_warnings()
|
||||||
|
|
||||||
def build_client(emulated_device: str = "MacBookPro18,3", emulated_app: str = "accountsd") -> str:
|
|
||||||
"""'Client Information'
|
|
||||||
String in the following format:
|
|
||||||
<%MODEL%> <%OS%;%MAJOR%.%MINOR%(%SPMAJOR%,%SPMINOR%);%BUILD%> <%AUTHKIT_BUNDLE_ID%/%AUTHKIT_VERSION% (%APP_BUNDLE_ID%/%APP_VERSION%)>
|
|
||||||
Where:
|
|
||||||
MODEL: The model of the device (e.g. MacBookPro15,1 or 'PC'
|
|
||||||
OS: The OS of the device (e.g. Mac OS X or Windows)
|
|
||||||
MAJOR: The major version of the OS (e.g. 10)
|
|
||||||
MINOR: The minor version of the OS (e.g. 15)
|
|
||||||
SPMAJOR: The major version of the service pack (e.g. 0) (Windows only)
|
|
||||||
SPMINOR: The minor version of the service pack (e.g. 0) (Windows only)
|
|
||||||
BUILD: The build number of the OS (e.g. 19C57)
|
|
||||||
AUTHKIT_BUNDLE_ID: The bundle ID of the AuthKit framework (e.g. com.apple.AuthKit)
|
|
||||||
AUTHKIT_VERSION: The version of the AuthKit framework (e.g. 1)
|
|
||||||
APP_BUNDLE_ID: The bundle ID of the app (e.g. com.apple.dt.Xcode)
|
|
||||||
APP_VERSION: The version of the app (e.g. 3594.4.19)
|
|
||||||
"""
|
|
||||||
|
|
||||||
model = emulated_device
|
def build_client(
|
||||||
if emulated_device == "PC":
|
emulated_device: str = "MacBookPro18,3", emulated_app: str = "accountsd"
|
||||||
# We're emulating a PC, so we run Windows (Vista?)
|
) -> str:
|
||||||
os = "Windows"
|
"""'Client Information'
|
||||||
os_version = "6.2(0,0);9200"
|
String in the following format:
|
||||||
else:
|
<%MODEL%> <%OS%;%MAJOR%.%MINOR%(%SPMAJOR%,%SPMINOR%);%BUILD%> <%AUTHKIT_BUNDLE_ID%/%AUTHKIT_VERSION% (%APP_BUNDLE_ID%/%APP_VERSION%)>
|
||||||
# We're emulating a Mac, so we run macOS Ventura
|
Where:
|
||||||
os = "Mac OS X"
|
MODEL: The model of the device (e.g. MacBookPro15,1 or 'PC'
|
||||||
os_version = "13.4.1;22F8"
|
OS: The OS of the device (e.g. Mac OS X or Windows)
|
||||||
|
MAJOR: The major version of the OS (e.g. 10)
|
||||||
|
MINOR: The minor version of the OS (e.g. 15)
|
||||||
|
SPMAJOR: The major version of the service pack (e.g. 0) (Windows only)
|
||||||
|
SPMINOR: The minor version of the service pack (e.g. 0) (Windows only)
|
||||||
|
BUILD: The build number of the OS (e.g. 19C57)
|
||||||
|
AUTHKIT_BUNDLE_ID: The bundle ID of the AuthKit framework (e.g. com.apple.AuthKit)
|
||||||
|
AUTHKIT_VERSION: The version of the AuthKit framework (e.g. 1)
|
||||||
|
APP_BUNDLE_ID: The bundle ID of the app (e.g. com.apple.dt.Xcode)
|
||||||
|
APP_VERSION: The version of the app (e.g. 3594.4.19)
|
||||||
|
"""
|
||||||
|
|
||||||
if emulated_app == "Xcode":
|
model = emulated_device
|
||||||
app_bundle = "com.apple.dt.Xcode"
|
if emulated_device == "PC":
|
||||||
app_version = "3594.4.19"
|
# We're emulating a PC, so we run Windows (Vista?)
|
||||||
elif emulated_app == "accountsd":
|
os = "Windows"
|
||||||
app_bundle = "com.apple.accountsd"
|
os_version = "6.2(0,0);9200"
|
||||||
app_version = "113"
|
else:
|
||||||
else:
|
# We're emulating a Mac, so we run macOS Ventura
|
||||||
app_bundle = "com.apple.iCloud"
|
os = "Mac OS X"
|
||||||
app_version = "7.21"
|
os_version = "13.4.1;22F8"
|
||||||
|
|
||||||
if os == "Windows":
|
if emulated_app == "Xcode":
|
||||||
authkit_bundle = "com.apple.AuthKitWin"
|
app_bundle = "com.apple.dt.Xcode"
|
||||||
authkit_version = "1"
|
app_version = "3594.4.19"
|
||||||
else:
|
elif emulated_app == "accountsd":
|
||||||
authkit_bundle = "com.apple.AOSKit"
|
app_bundle = "com.apple.accountsd"
|
||||||
authkit_version = "282"
|
app_version = "113"
|
||||||
|
else:
|
||||||
|
app_bundle = "com.apple.iCloud"
|
||||||
|
app_version = "7.21"
|
||||||
|
|
||||||
|
if os == "Windows":
|
||||||
|
authkit_bundle = "com.apple.AuthKitWin"
|
||||||
|
authkit_version = "1"
|
||||||
|
else:
|
||||||
|
authkit_bundle = "com.apple.AOSKit"
|
||||||
|
authkit_version = "282"
|
||||||
|
|
||||||
|
return f"<{model}> <{os};{os_version}> <{authkit_bundle}/{authkit_version} ({app_bundle}/{app_version})>"
|
||||||
|
|
||||||
return f"<{model}> <{os};{os_version}> <{authkit_bundle}/{authkit_version} ({app_bundle}/{app_version})>"
|
|
||||||
|
|
||||||
def _generate_cpd() -> dict:
|
def _generate_cpd() -> dict:
|
||||||
cpd = {
|
cpd = {
|
||||||
|
@ -112,22 +116,30 @@ def _generate_cpd() -> dict:
|
||||||
cpd.update(generate_anisette_headers())
|
cpd.update(generate_anisette_headers())
|
||||||
return cpd
|
return cpd
|
||||||
|
|
||||||
def _generate_meta_headers(serial: str = "0", user_id: uuid = uuid.uuid4(), device_id: uuid = uuid.uuid4()) -> dict:
|
|
||||||
return {
|
|
||||||
"X-Apple-I-Client-Time": datetime.utcnow().replace(microsecond=0).isoformat() + "Z", # Current timestamp in ISO 8601 format
|
|
||||||
"X-Apple-I-TimeZone": str(datetime.utcnow().astimezone().tzinfo), # Abbreviation of the timezone of the device (e.g. EST)
|
|
||||||
|
|
||||||
|
def _generate_meta_headers(
|
||||||
|
serial: str = "0", user_id: uuid = uuid.uuid4(), device_id: uuid = uuid.uuid4()
|
||||||
|
) -> dict:
|
||||||
|
return {
|
||||||
|
"X-Apple-I-Client-Time": datetime.utcnow().replace(microsecond=0).isoformat()
|
||||||
|
+ "Z", # Current timestamp in ISO 8601 format
|
||||||
|
"X-Apple-I-TimeZone": str(
|
||||||
|
datetime.utcnow().astimezone().tzinfo
|
||||||
|
), # Abbreviation of the timezone of the device (e.g. EST)
|
||||||
# Locale of the device (e.g. en_US)
|
# Locale of the device (e.g. en_US)
|
||||||
"loc": locale.getdefaultlocale()[0] or "en_US",
|
"loc": locale.getdefaultlocale()[0] or "en_US",
|
||||||
"X-Apple-Locale": locale.getdefaultlocale()[0] or "en_US",
|
"X-Apple-Locale": locale.getdefaultlocale()[0] or "en_US",
|
||||||
|
"X-Apple-I-MD-RINFO": "17106176", # either 17106176 or 50660608
|
||||||
"X-Apple-I-MD-RINFO": "17106176", # either 17106176 or 50660608
|
"X-Apple-I-MD-LU": b64encode(
|
||||||
|
str(user_id).upper().encode()
|
||||||
"X-Apple-I-MD-LU": b64encode(str(user_id).upper().encode()).decode(), # 'Local User ID': Base64 encoding of an uppercase UUID
|
).decode(), # 'Local User ID': Base64 encoding of an uppercase UUID
|
||||||
"X-Mme-Device-Id": str(device_id).upper(), # 'Device Unique Identifier', uppercase UUID
|
"X-Mme-Device-Id": str(
|
||||||
"X-Apple-I-SRL-NO": serial, # Serial number
|
device_id
|
||||||
|
).upper(), # 'Device Unique Identifier', uppercase UUID
|
||||||
|
"X-Apple-I-SRL-NO": serial, # Serial number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _generate_local_anisette() -> dict:
|
def _generate_local_anisette() -> dict:
|
||||||
logger.debug("Using local anisette generation")
|
logger.debug("Using local anisette generation")
|
||||||
"""Generates anisette data using AOSKit locally"""
|
"""Generates anisette data using AOSKit locally"""
|
||||||
|
@ -148,6 +160,7 @@ def _generate_local_anisette() -> dict:
|
||||||
"X-Apple-I-MD-M": str(h["X-Apple-MD-M"]),
|
"X-Apple-I-MD-M": str(h["X-Apple-MD-M"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _generate_remote_anisette(url: str) -> dict:
|
def _generate_remote_anisette(url: str) -> dict:
|
||||||
logger.debug("Using remote anisette generation: " + url)
|
logger.debug("Using remote anisette generation: " + url)
|
||||||
h = json.loads(requests.get(url, timeout=5).text)
|
h = json.loads(requests.get(url, timeout=5).text)
|
||||||
|
@ -156,11 +169,12 @@ def _generate_remote_anisette(url: str) -> dict:
|
||||||
"X-Apple-I-MD-M": h["X-Apple-I-MD-M"],
|
"X-Apple-I-MD-M": h["X-Apple-I-MD-M"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def generate_anisette_headers() -> dict:
|
def generate_anisette_headers() -> dict:
|
||||||
if isinstance(ANISETTE, str) and ANISETTE.startswith("http"):
|
if isinstance(ANISETTE, str) and ANISETTE.startswith("http"):
|
||||||
a = _generate_remote_anisette(ANISETTE)
|
a = _generate_remote_anisette(ANISETTE)
|
||||||
else:
|
else:
|
||||||
a =_generate_local_anisette()
|
a = _generate_local_anisette()
|
||||||
|
|
||||||
a.update(_generate_meta_headers(user_id=USER_ID, device_id=DEVICE_ID))
|
a.update(_generate_meta_headers(user_id=USER_ID, device_id=DEVICE_ID))
|
||||||
return a
|
return a
|
||||||
|
@ -205,8 +219,8 @@ def check_error(r):
|
||||||
|
|
||||||
if status["ec"] != 0:
|
if status["ec"] != 0:
|
||||||
raise Exception(f"Error {status['ec']}: {status['em']}")
|
raise Exception(f"Error {status['ec']}: {status['em']}")
|
||||||
#print(f"Error {status['ec']}: {status['em']}")
|
# print(f"Error {status['ec']}: {status['em']}")
|
||||||
#return True
|
# return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@ -248,7 +262,7 @@ def trusted_second_factor(dsid, idms_token):
|
||||||
"X-Apple-Identity-Token": identity_token,
|
"X-Apple-Identity-Token": identity_token,
|
||||||
"X-Apple-App-Info": "com.apple.gs.xcode.auth",
|
"X-Apple-App-Info": "com.apple.gs.xcode.auth",
|
||||||
"X-Xcode-Version": "11.2 (11B41)",
|
"X-Xcode-Version": "11.2 (11B41)",
|
||||||
"X-Mme-Client-Info": build_client(emulated_app="Xcode")
|
"X-Mme-Client-Info": build_client(emulated_app="Xcode"),
|
||||||
}
|
}
|
||||||
|
|
||||||
headers.update(generate_anisette_headers())
|
headers.update(generate_anisette_headers())
|
||||||
|
@ -296,7 +310,7 @@ def sms_second_factor(dsid, idms_token):
|
||||||
"X-Apple-Identity-Token": identity_token,
|
"X-Apple-Identity-Token": identity_token,
|
||||||
"X-Apple-App-Info": "com.apple.gs.xcode.auth",
|
"X-Apple-App-Info": "com.apple.gs.xcode.auth",
|
||||||
"X-Xcode-Version": "11.2 (11B41)",
|
"X-Xcode-Version": "11.2 (11B41)",
|
||||||
"X-Mme-Client-Info": build_client(emulated_app="Xcode")
|
"X-Mme-Client-Info": build_client(emulated_app="Xcode"),
|
||||||
}
|
}
|
||||||
|
|
||||||
headers.update(generate_anisette_headers())
|
headers.update(generate_anisette_headers())
|
||||||
|
@ -359,7 +373,9 @@ def authenticate(username, password):
|
||||||
return
|
return
|
||||||
|
|
||||||
if r["sp"] != "s2k":
|
if r["sp"] != "s2k":
|
||||||
logger.error(f"This implementation only supports s2k. Server returned {r['sp']}")
|
logger.error(
|
||||||
|
f"This implementation only supports s2k. Server returned {r['sp']}"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Change the password out from under the SRP library, as we couldn't calculate it without the salt.
|
# Change the password out from under the SRP library, as we couldn't calculate it without the salt.
|
||||||
|
|
Loading…
Reference in a new issue