Skip to content

Commit

Permalink
fix: New AppleID Auth with SIRP
Browse files Browse the repository at this point in the history
  • Loading branch information
iowk committed Oct 22, 2024
1 parent 6e9c6c6 commit 4bcb2ac
Showing 1 changed file with 63 additions and 11 deletions.
74 changes: 63 additions & 11 deletions src/pyicloud_ipd/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
from re import match
import http.cookiejar as cookielib
import getpass
import srp
import base64
import hashlib

from requests import PreparedRequest, Request, Response

Expand Down Expand Up @@ -190,26 +193,75 @@ def authenticate(self, force_refresh:bool=False, service:Optional[Any]=None) ->
if not login_successful:
LOGGER.debug("Authenticating as %s", self.user["accountName"])

data = dict(self.user)

data["rememberMe"] = True
data["trustTokens"] = []
if self.session_data.get("trust_token"):
data["trustTokens"] = [self.session_data.get("trust_token")]

headers = self._get_auth_headers()

scnt = self.session_data.get("scnt")
if scnt:
headers["scnt"] = scnt

session_id = self.session_data.get("session_id")
if session_id:
headers["X-Apple-ID-Session-Id"] = session_id

class SrpPassword():
def __init__(self, password: str):
self.password = password

def set_encrypt_info(self, salt: bytes, iterations: int, key_length: int):
self.salt = salt
self.iterations = iterations
self.key_length = key_length

def encode(self):
password_hash = hashlib.sha256(self.password.encode('utf-8')).digest()
return hashlib.pbkdf2_hmac('sha256', password_hash, salt, iterations, key_length)

srp_password = SrpPassword(self.user["password"])
srp.rfc5054_enable()
srp.no_username_in_x()
usr = srp.User(self.user["accountName"], srp_password, hash_alg=srp.SHA256, ng_type=srp.NG_2048)

uname, A = usr.start_authentication()

data = {
'a': base64.b64encode(A).decode(),
'accountName': uname,
'protocols': ['s2k', 's2k_fo']
}

try:
response = self.session.post("%s/signin/init" % self.AUTH_ENDPOINT, data=json.dumps(data), headers=headers)
response.raise_for_status()
except PyiCloudAPIResponseException as error:
msg = "Failed to initiate srp authentication."
raise PyiCloudFailedLoginException(msg, error) from error

body = response.json()

salt = base64.b64decode(body['salt'])
b = base64.b64decode(body['b'])
c = body['c']
iterations = body['iteration']
key_length = 32
srp_password.set_encrypt_info(salt, iterations, key_length)

m1 = usr.process_challenge( salt, b )
m2 = usr.H_AMK

data = {
"accountName": uname,
"c": c,
"m1": base64.b64encode(m1).decode(),
"m2": base64.b64encode(m2).decode(),
"rememberMe": True,
"trustTokens": [],
}

if self.session_data.get("trust_token"):
data["trustTokens"] = [self.session_data.get("trust_token")]

try:
self.session.post(
"%s/signin" % self.AUTH_ENDPOINT,
"%s/signin/complete" % self.AUTH_ENDPOINT,
params={"isRememberMeEnabled": "true"},
data=json.dumps(data),
headers=headers,
Expand Down Expand Up @@ -284,7 +336,7 @@ def _validate_token(self) -> Dict[str, Any]:

def _get_auth_headers(self, overrides: Optional[Dict[str, str]]=None) -> Dict[str, str]:
headers = {
"Accept": "*/*",
"Accept": "application/json, text/javascript",
"Content-Type": "application/json",
"X-Apple-OAuth-Client-Id": "d39ba9916b7251055b22c7f910e2ea796ee65e98b2ddecea8f5dde8d9d1a815d",
"X-Apple-OAuth-Client-Type": "firstPartyAuth",
Expand Down

4 comments on commit 4bcb2ac

@svenhofman
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @iowk ,

Could you explain to me how you devised this solution? Specifically, how did you determine that the password needs to be encoded (done in SrpPassword.encode()), and that the username should be an empty string in the x, the private key.

Thanks in advance!

-- Sven

@iowk
Copy link
Contributor Author

@iowk iowk commented on 4bcb2ac Oct 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @iowk ,

Could you explain to me how you devised this solution? Specifically, how did you determine that the password needs to be encoded (done in SrpPassword.encode()), and that the username should be an empty string in the x, the private key.

Thanks in advance!

-- Sven

I took fastlane's fix fastlane/fastlane@faee8dc as a reference

@svenhofman
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick reply. Also, any idea why we send m2 to the server? I'm not too familiar with the protocol, but isn't this usually what is sent by the server and verified by the client?

Is there a resource which explains the process of iCloud authentication?

@iowk
Copy link
Contributor Author

@iowk iowk commented on 4bcb2ac Oct 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, but there is no m2 from the server response, and I guess that's how Apple design it. What I did is just rewrite fastlane's commit in Python.

I don't think there's a document about it.

Please sign in to comment.