3232logger = logging .getLogger ()
3333
3434
35- def icloud_login_mobileme (username = '' , password = '' , second_factor = 'sms' ):
35+ def icloud_login_mobileme (username = '' , password = '' ):
3636 if not username :
3737 username = input ('Apple ID: ' )
3838 if not password :
3939 password = getpass ('Password: ' )
40- g = gsa_authenticate (username , password , second_factor )
40+ g = gsa_authenticate (username , password )
4141 pet = g ["t" ]["com.apple.gs.idms.pet" ]["token" ]
4242 adsid = g ["adsid" ]
4343
@@ -55,21 +55,24 @@ def icloud_login_mobileme(username='', password='', second_factor='sms'):
5555 }
5656 headers .update (generate_anisette_headers ())
5757
58- r = requests .post (
58+ logger .info ("Registering device after login" )
59+ resp = requests .post (
5960 "https://setup.icloud.com/setup/iosbuddy/loginDelegates" ,
6061 auth = (username , pet ),
6162 data = data ,
6263 headers = headers ,
6364 verify = False ,
6465 )
65- return plist .loads (r .content )
66+ response = f"HTTP-Code: { resp .status_code } \n { resp .text } "
67+ logger .debug (response )
68+ return plist .loads (resp .content )
6669
6770
68- def gsa_authenticate (username , password , second_factor = 'sms' ):
71+ def gsa_authenticate (username , password ):
6972 # Password is None as we'll provide it later
7073 usr = srp .User (username , bytes (), hash_alg = srp .SHA256 , ng_type = srp .NG_2048 )
7174 _ , A = usr .start_authentication ()
72-
75+ logger . info ( "Authentication request initialization" )
7376 r = gsa_authenticated_request (
7477 {"A2k" : A , "ps" : ["s2k" , "s2k_fo" ], "u" : username , "o" : "init" })
7578
@@ -87,36 +90,38 @@ def gsa_authenticate(username, password, second_factor='sms'):
8790 if M is None :
8891 logger .error ("Failed to process challenge" )
8992 return
90-
91- r = gsa_authenticated_request (
93+ logger . info ( "Authentication request completion" )
94+ resp = gsa_authenticated_request (
9295 {"c" : r ["c" ], "M1" : M , "u" : username , "o" : "complete" })
9396
9497 # Make sure that the server's session key matches our session key (and thus that they are not an imposter)
95- usr .verify_session (r ["M2" ])
98+ if "M2" not in resp :
99+ logger .error ("Error on authentication" )
100+ logger .error (resp )
101+ return
102+ usr .verify_session (resp ["M2" ])
96103 if not usr .authenticated ():
97104 logger .error ("Failed to verify session" )
98105 return
99106
100- spd = decrypt_cbc (usr , r ["spd" ])
107+ spd = decrypt_cbc (usr , resp ["spd" ])
101108 # For some reason plistlib doesn't accept it without the header...
102109 PLISTHEADER = b"""\
103110 <?xml version='1.0' encoding='UTF-8'?>
104111<!DOCTYPE plist PUBLIC '-//Apple//DTD PLIST 1.0//EN' 'http://www.apple.com/DTDs/PropertyList-1.0.dtd'>
105112"""
106113 spd = plist .loads (PLISTHEADER + spd )
107114
108- if "au" in r ["Status" ] and r ["Status" ]["au" ] in ["trustedDeviceSecondaryAuth" , "secondaryAuth" ]:
109- logger .info ("2FA required, requesting code" )
115+ if "au" in resp ["Status" ] and resp ["Status" ]["au" ] in ["trustedDeviceSecondaryAuth" , "secondaryAuth" ]:
116+ logger .info ("2FA required, requesting SMS code. (No other 2FA-code will work!) " )
110117 # Replace bytes with strings
111118 for k , v in spd .items ():
112119 if isinstance (v , bytes ):
113120 spd [k ] = base64 .b64encode (v ).decode ()
114- if second_factor == 'sms' :
115- sms_second_factor (spd ["adsid" ], spd ["GsIdmsToken" ])
116- elif second_factor == 'trusted_device' :
117- trusted_second_factor (spd ["adsid" ], spd ["GsIdmsToken" ])
121+ sms_second_factor (spd ["adsid" ], spd ["GsIdmsToken" ])
122+
118123 return gsa_authenticate (username , password )
119- elif "au" in r ["Status" ]:
124+ elif "au" in resp ["Status" ]:
120125 logger .error (f"Unknown auth value { r ['Status' ]['au' ]} " )
121126 return
122127 else :
@@ -144,6 +149,8 @@ def gsa_authenticated_request(parameters):
144149 verify = False ,
145150 timeout = 5 ,
146151 )
152+ response = f"HTTP-Code: { resp .status_code } \n { resp .text } "
153+ logger .debug (response )
147154
148155 return plist .loads (resp .content )["Response" ]
149156
@@ -163,10 +170,6 @@ def generate_cpd():
163170
164171
165172def generate_anisette_headers ():
166-
167-
168- logger .debug (
169- f'Querying { config .getAnisetteServer ()} for an anisette server' )
170173 h = json .loads (requests .get (config .getAnisetteServer (), timeout = 5 ).text )
171174 a = {"X-Apple-I-MD" : h ["X-Apple-I-MD" ],
172175 "X-Apple-I-MD-M" : h ["X-Apple-I-MD-M" ]}
@@ -214,48 +217,6 @@ def decrypt_cbc(usr, data):
214217 return padder .update (data ) + padder .finalize ()
215218
216219
217- def trusted_second_factor (dsid , idms_token ):
218- identity_token = base64 .b64encode (
219- (dsid + ":" + idms_token ).encode ()).decode ()
220-
221- headers = {
222- "Content-Type" : "text/x-xml-plist" ,
223- "User-Agent" : "Xcode" ,
224- "Accept" : "text/x-xml-plist" ,
225- "Accept-Language" : "en-us" ,
226- "X-Apple-Identity-Token" : identity_token ,
227- "X-Apple-App-Info" : "com.apple.gs.xcode.auth" ,
228- "X-Xcode-Version" : "11.2 (11B41)" ,
229- "X-Mme-Client-Info" : '<MacBookPro18,3> <Mac OS X;13.4.1;22F8> <com.apple.AOSKit/282 (com.apple.dt.Xcode/3594.4.19)>'
230- }
231-
232- headers .update (generate_anisette_headers ())
233-
234- # This will trigger the 2FA prompt on trusted devices
235- # We don't care about the response, it's just some HTML with a form for entering the code
236- # Easier to just use a text prompt
237- requests .get (
238- "https://gsa.apple.com/auth/verify/trusteddevice" ,
239- headers = headers ,
240- verify = False ,
241- timeout = 10 ,
242- )
243-
244- # Prompt for the 2FA code. It's just a string like '123456', no dashes or spaces
245- code = getpass ("Enter 2FA code: " )
246- headers ["security-code" ] = code
247-
248- # Send the 2FA code to Apple
249- resp = requests .get (
250- "https://gsa.apple.com/grandslam/GsService2/validate" ,
251- headers = headers ,
252- verify = False ,
253- timeout = 10 ,
254- )
255- if resp .ok :
256- logger .info ("2FA successful" )
257-
258-
259220def sms_second_factor (dsid , idms_token ):
260221 identity_token = base64 .b64encode (
261222 (dsid + ":" + idms_token ).encode ()).decode ()
@@ -290,7 +251,7 @@ def sms_second_factor(dsid, idms_token):
290251 timeout = 5
291252 )
292253 # Prompt for the 2FA code. It's just a string like '123456', no dashes or spaces
293- code = input ("Enter 2FA code: " )
254+ code = input ("Enter SMS 2FA code: " )
294255
295256 body ['securityCode' ] = {'code' : code }
296257
@@ -302,5 +263,15 @@ def sms_second_factor(dsid, idms_token):
302263 verify = False ,
303264 timeout = 5 ,
304265 )
305- if resp .ok :
266+ response = f"HTTP-Code: { resp .status_code } with { len (resp .text )} bytes"
267+ logger .debug (response )
268+ header_string = "Headers:\n "
269+ for header , value in resp .headers .items ():
270+ header_string += f"{ header } : { value } \n "
271+ logger .debug (header_string )
272+ # Headers does not include Apple DSID, 2FA failed
273+ if resp .ok and "X-Apple-DSID" in resp .headers :
306274 logger .info ("2FA successful" )
275+ else :
276+ raise Exception (
277+ "2FA unsuccessful. Maybe wrong code or wrong number. Check your account details." )
0 commit comments