#include "appleauth/iauth.h" #include "tier2/fileformats/plist.h" #include "http/http.h" #include "openssl/srp.h" #include "openssl/evp.h" #include "openssl/sha.h" #include "tier0/rand.h" IHTTPClientManager *g_pHttpClientMgr = NULL; static int base64_decode(const char *b64, unsigned char *pOut) { int len = strlen(b64); if (pOut == NULL) return (len*3)/4; int out_len = EVP_DecodeBlock(pOut, (const unsigned char *)b64, len); if (out_len < 0) return -1; while (len > 0 && b64[len - 1] == '=') { out_len--; len--; } return out_len; } // autogenerated #define APPLE_LOCAL_USER "e2e70285da39596ef06153b9c4e1e5dc8d2f983bc5cd63f5b1e292207060d931" #define APPLE_HTTP_HEADER \ { \ {"X-Apple-I-Client-Time", "2026-1-9T12:00:00"}, \ {"X-Apple-Locale", "en_US"}, \ {"X-Apple-I-TimeZone", "Europe/Kyiv"}, \ {"X-Mme-Client-Info", " "}, \ {"User-Agent", "akd/1.0 CFNetwork/808.1.4"}, \ {"X-Apple-I-MD-LU", APPLE_LOCAL_USER}, \ {"X-Apple-I-MD-M", "0"}, \ {"X-Apple-I-SRL-NO", "0"}, \ {"X-Mme-Device-Id", "0"}, \ {"Content-Type", "text/x-xml-plist"}, \ {"Accept", "*/*"}, \ }; #define N2048 "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73" #define G2048 "2" enum EPasswordType { PASSWORD_TYPE_S2K, PASSWORD_TYPE_S2K_FO, }; class CAppleAuth: public IAppleAuth { public: virtual void Init() override; virtual void Shutdown() override; virtual void LaunchLoginDaemon() override; virtual EAppleAuthDaemonStatus GetStatus() override; virtual EAppleAuthStatus SubmitLoginData( const char *szEmail, const char *szPassword ) override; virtual EAppleAuthStatus Submit2FA( const char *szCode ) override; virtual CUtlString EncryptPassword( const char *szPassword, CUtlString szSalt, uint32_t uIters, EPasswordType eType ); void ComputeM1( unsigned char M1[SHA256_DIGEST_LENGTH], const BIGNUM *N, const BIGNUM *g, const char *username, const char *password, const unsigned char *salt, size_t salt_len, const BIGNUM *A, const BIGNUM *B, const BIGNUM *a ); CUtlString FetchADIPB(); void FetchHeaders( CUtlString szAdiPb ); IHTTPClient *m_pANIClient; IHTTPClient *m_pGrandSlamClient; CUtlString m_szProvisionStart = "/grandslam/MidService/startMachineProvisioning"; CUtlString m_szProvisionFinish = "/grandslam/MidService/finishMachineProvisioning"; IJSONObject *pHeader; CUtlString m_szAppleIMD; CUtlString m_szAppleIMDM; CUtlString m_szAppleIMDRINFO; }; CUtlString CAppleAuth::FetchADIPB() { CUtlString szProvisionExpectedInputRaw = NULL; IJSONObject *pObject = NULL; IJSONObject *pPlistObject = NULL; CUtlString szProvisionExpectedInput = NULL; CUtlString szAdiPb = NULL; m_pANIClient = g_pHttpClientMgr->Connect("ani.sidestore.io", true, NULL); { HTTPHeaderParam_t params[] = APPLE_HTTP_HEADER; HTTPHeader_t header = { sizeof(params)/sizeof(HTTPHeaderParam_t), params, }; m_pGrandSlamClient->Get("/grandslam/GsService2/lookup", &header); HTTPResponse_t r = m_pGrandSlamClient->GetResponse(); if ( r.m_uCode != 200 ) { g_pHttpClientMgr->Disconnect(m_pANIClient); return NULL; } } if ( !m_pANIClient->WebSocket_Connect("/v3/provisioning_session") ) Plat_FatalErrorFunc("Failed to init AppleAuth websocket\n"); // Getting adipb while ( ( szProvisionExpectedInputRaw = m_pANIClient->WebSocket_RecvText() ) != NULL ) { pObject = JSONManager()->ReadString(szProvisionExpectedInputRaw); szProvisionExpectedInput = pObject->GetValue("result")->GetStringValue(); V_printf("%s\n",szProvisionExpectedInput.GetString()); if ( szProvisionExpectedInput == "GiveIdentifier" ) { m_pANIClient->WebSocket_SendText("{\"identifier\": \"" APPLE_LOCAL_USER "\"}"); continue; } if ( szProvisionExpectedInput == "GiveStartProvisioningData" ) { const char *plist = "\n" "\n" "\n" "\n" "\tHeader\n" "\t\n" "\tRequest\n" "\t\n" "\n" "\n"; HTTPHeaderParam_t params[] = APPLE_HTTP_HEADER; HTTPHeader_t header = { sizeof(params)/sizeof(HTTPHeaderParam_t), params, }; m_pGrandSlamClient->Post(m_szProvisionStart, &header, V_strlen(plist), plist); HTTPResponse_t r = m_pGrandSlamClient->GetResponse(); if ( r.m_uCode != 200 ) { g_pHttpClientMgr->Disconnect(m_pANIClient); return NULL; } IJSONObject *pObject = PropertyListManager()->ReadString(r.m_message); CUtlString spim = pObject->GetValue("Response")->GetObject()->GetValue("spim")->GetStringValue(); m_pANIClient->WebSocket_SendText(CUtlString("{\"spim\": \"%s\"}", spim.GetString())); continue; } if ( szProvisionExpectedInput == "GiveEndProvisioningData" ) { CUtlString cpim = pObject->GetValue("cpim")->GetStringValue(); CUtlString plist = CUtlString( "\n" "\n" "\n" "\n" "\tHeader\n" "\t\n" "\tRequest\n" "\t\n" "\tcpim\n" "\t%s\n" "\t\n" "\n" "\n", cpim.GetString()); HTTPHeaderParam_t params[] = APPLE_HTTP_HEADER; HTTPHeader_t header = { sizeof(params)/sizeof(HTTPHeaderParam_t), params, }; m_pGrandSlamClient->Post(m_szProvisionFinish, &header, V_strlen(plist), plist); HTTPResponse_t r = m_pGrandSlamClient->GetResponse(); if ( r.m_uCode != 200 ) { g_pHttpClientMgr->Disconnect(m_pANIClient); return NULL; } IJSONObject *pObject = PropertyListManager()->ReadString(r.m_message); CUtlString tk = pObject->GetValue("Response")->GetObject()->GetValue("tk")->GetStringValue(); CUtlString ptm = pObject->GetValue("Response")->GetObject()->GetValue("ptm")->GetStringValue(); V_printf("%s\n",CUtlString("{\"tk\": \"%s\", \"ptm\": \"%s\"}", tk.GetString(), ptm.GetString()).GetString()); m_pANIClient->WebSocket_SendText(CUtlString("{\"tk\": \"%s\", \"ptm\": \"%s\"}", tk.GetString(), ptm.GetString())); continue; } if ( szProvisionExpectedInput == "ProvisioningSuccess" ) { szAdiPb = pObject->GetValue("adi_pb")->GetStringValue(); g_pHttpClientMgr->Disconnect(m_pANIClient); break; } if ( szProvisionExpectedInput == "Timeout" ) { break; } g_pHttpClientMgr->Disconnect(m_pANIClient); return NULL; } g_pHttpClientMgr->Disconnect(m_pANIClient); return szAdiPb; } void CAppleAuth::FetchHeaders( CUtlString szAdiPb ) { // Get header HTTPHeaderParam_t params[] = { "Content-Type", "application/json", }; HTTPHeader_t header = { sizeof(params)/sizeof(HTTPHeaderParam_t), params, }; m_pANIClient = g_pHttpClientMgr->Connect("ani.sidestore.io", true, NULL); CUtlString szPostData = CUtlString("{\"identifier\": \"" APPLE_LOCAL_USER "\", \"adi_pb\": \"%s\"}", szAdiPb.GetString()); V_printf("%s\n",szPostData.GetString()); m_pANIClient->Post("/v3/get_headers", &header, V_strlen(szPostData), szPostData); HTTPResponse_t r = m_pANIClient->GetResponse(); V_printf("%i\n",r.m_uCode); if ( r.m_uCode == 200 ) { V_printf("%s\n",r.m_message.GetMemory()); pHeader = JSONManager()->ReadString(r.m_message); m_szAppleIMD = pHeader->GetValue("X-Apple-I-MD")->GetStringValue(); m_szAppleIMDM = pHeader->GetValue("X-Apple-I-MD-M")->GetStringValue(); m_szAppleIMDRINFO = pHeader->GetValue("X-Apple-I-MD-RINFO")->GetStringValue(); JSONManager()->FreeObject(pHeader); } g_pHttpClientMgr->Disconnect(m_pANIClient); } void CAppleAuth::Init() { CreateInterfaceFn fnHttpFactory = Sys_GetFactory("funnyhttp"); g_pHttpClientMgr = (IHTTPClientManager*)fnHttpFactory(HTTP_CLIENT_INTERFACE_VERSION, NULL); m_pGrandSlamClient = g_pHttpClientMgr->Connect("gsa.apple.com", true, NULL); CUtlString szAdiPb = FetchADIPB(); FetchHeaders(szAdiPb); } void CAppleAuth::Shutdown() { } void CAppleAuth::LaunchLoginDaemon() { } EAppleAuthDaemonStatus CAppleAuth::GetStatus() { return APPLE_AUTH_DAEMON_NOT_LOGGED_IN; } EAppleAuthStatus CAppleAuth::SubmitLoginData( const char *szEmail, const char *szPassword ) { const char *pszUser = szEmail; const char *pszPass = szPassword; BIGNUM* pN = BN_new(); BIGNUM* pG = BN_new(); BIGNUM* pa = BN_new(); BIGNUM* pA = BN_new(); BN_CTX *pbnCtx = BN_CTX_new(); SHA256_CTX pSha; unsigned char pK[SHA256_DIGEST_LENGTH]; BN_hex2bn(&pN, N2048); BN_hex2bn(&pG, G2048); CUtlBuffer bN; CUtlBuffer bG; bN = CUtlBuffer(BN_num_bytes(pN)); bG = CUtlBuffer(BN_num_bytes(pG)); BN_bn2bin(pN, bN.GetMemory()); SHA256_Init(&pSha); SHA256_Update(&pSha, bN.GetMemory(), bN.GetSize()); SHA256_Update(&pSha, bG.GetMemory(), bG.GetSize()); SHA256_Final(pK, &pSha); BN_rand(pa, 256, 0, 0); BN_set_flags(pa, BN_FLG_CONSTTIME); BN_mod_exp(pA, pG, pa, pN, pbnCtx); char *pszA = BN_bn2hex(pA); CUtlString plist = CUtlString( "\n" "\n" "\n" "\n" "\tHeader\n" "\t\n" "\tVersion\n" "\t1.0.1\n" "\t\n" "\tRequest\n" "\t\n" "\tA2k\n" "\t%s\n" "\tps\n" "\ts2ks2k_fo\n" "\tu\n" "\t%s\n" "\to\n" "\tinit\n" "\tcpd\n" "\t\n" "\tX-Apple-I-Client-Time\n" "\t2026-1-9T12:00:00Z\n" "\tX-Apple-I-MD\n" "\t%s\n" "\tX-Apple-I-MD-LU\n" "\t" APPLE_LOCAL_USER "\n" "\tX-Apple-I-MD-M\n" "\t%s\n" "\tX-Apple-I-MD-RINFO\n" "\t%s\n" "\tX-Mme-Client-Info\n" "\t<MacBookPro13,2> <macOS;13.1;22C65> <com.apple.AuthKit/1 (com.apple.dt.Xcode/3594.4.19)>\n" "\tX-Apple-I-SRL-NO\n" "\t0\n" "\tbootstrap\n" "\t\n" "\ticscrec\n" "\t\n" "\tpbe\n" "\t\n" "\tprkgen\n" "\t\n" "\tsvct\n" "\tiCloud\n" "\tloc\n" "\ten_US\n" "\t\n" "\t\n" "\n" "\n", pszA, szEmail, m_szAppleIMD.GetString(), m_szAppleIMDM.GetString(), m_szAppleIMDRINFO.GetString()); HTTPHeaderParam_t params[] = { {"Content-Type", "text/x-xml-plist"}, {"Accept", "*/*"}, {"User-Agent", "akd/1.0 CFNetwork/808.1.4"}, \ {"X-Mme-Client-Info", " "}, \ }; HTTPHeader_t header = { sizeof(params)/sizeof(HTTPHeaderParam_t), params, }; V_printf("%s\n",plist.GetString()); m_pGrandSlamClient->Post("/grandslam/GsService2", &header, plist.GetLenght(), plist); HTTPResponse_t stResponse = m_pGrandSlamClient->GetResponse(); V_printf("%i %i\n", stResponse.m_uCode, stResponse.m_bIsComplete); if (stResponse.m_uCode != 200) return APPLE_AUTH_FAILURE; V_printf("%s\n",stResponse.m_message.GetMemory()); IJSONObject *pObject = PropertyListManager()->ReadString(stResponse.m_message); IJSONObject *pResponse = pObject->GetValue("Response")->GetObject(); CUtlString szEncryptedPassword = EncryptPassword(szPassword, pResponse->GetValue("s")->GetStringValue(), pResponse->GetValue("i")->GetNumberValue(), PASSWORD_TYPE_S2K); const char *szSalt = pResponse->GetValue("s")->GetStringValue(); const char *szB = pResponse->GetValue("B")->GetStringValue(); unsigned char salt[32]; int iSaltLen = base64_decode(szSalt, salt); int iBLen = base64_decode(szB, NULL); CUtlBuffer BBuffer = CUtlBuffer(iBLen); iBLen = base64_decode(szB, BBuffer.GetMemory()); BIGNUM *pB = BN_bin2bn(BBuffer.GetMemory(), BBuffer.GetSize(), NULL); unsigned char M1[SHA256_DIGEST_LENGTH]; ComputeM1(M1, pN, pG, szEmail, szPassword, salt, iSaltLen, pA, pB, pa); CUtlString szM1 = {}; for ( int i = 0; i < SHA256_DIGEST_LENGTH; i++ ) { szM1.AppendTail(CUtlString("%02x", M1[i])); } plist = CUtlString( "\n" "\n" "\n" "\n" "\tHeader\n" "\t\n" "\tVersion\n" "\t1.0.1\n" "\t\n" "\tRequest\n" "\t\n" "\tM1\n" "\t%s\n" "\tc\n" "\t%s\n" "\tps\n" "\ts2ks2k_fo\n" "\tu\n" "\t%s\n" "\to\n" "\tcomplete\n" "\tcpd\n" "\t\n" "\tX-Apple-I-Client-Time\n" "\t2026-1-9T12:00:00Z\n" "\tX-Apple-I-MD\n" "\t%s\n" "\tX-Apple-I-MD-LU\n" "\t" APPLE_LOCAL_USER "\n" "\tX-Apple-I-MD-M\n" "\t%s\n" "\tX-Apple-I-MD-RINFO\n" "\t%s\n" "\tX-Mme-Client-Info\n" "\t<MacBookPro13,2> <macOS;13.1;22C65> <com.apple.AuthKit/1 (com.apple.dt.Xcode/3594.4.19)>\n" "\tX-Apple-I-SRL-NO\n" "\t0\n" "\tbootstrap\n" "\t\n" "\ticscrec\n" "\t\n" "\tpbe\n" "\t\n" "\tprkgen\n" "\t\n" "\tsvct\n" "\tiCloud\n" "\tloc\n" "\ten_US\n" "\t\n" "\t\n" "\n" "\n", szM1.GetString(), pResponse->GetValue("c")->GetStringValue(), szEmail, m_szAppleIMD.GetString(), m_szAppleIMDM.GetString(), m_szAppleIMDRINFO.GetString()); V_printf("%s\n", plist.GetString()); m_pGrandSlamClient->Post("/grandslam/GsService2", &header, plist.GetLenght(), plist); stResponse = m_pGrandSlamClient->GetResponse(); V_printf("%i %i\n", stResponse.m_uCode, stResponse.m_bIsComplete); if (stResponse.m_uCode != 200) return APPLE_AUTH_FAILURE; V_printf("%s\n", stResponse.m_message.GetMemory()); g_pHttpClientMgr->Disconnect(m_pGrandSlamClient); OPENSSL_free(pszA); return APPLE_AUTH_SUCCESS; } static void hash_bn_pad(unsigned char out[SHA256_DIGEST_LENGTH], const BIGNUM *bn) { unsigned char buf[256]; memset(buf, 0, sizeof(buf)); int len = BN_num_bytes(bn); BN_bn2bin(bn, buf + (256 - len)); SHA256(buf, 256, out); } void CAppleAuth::ComputeM1( unsigned char M1[SHA256_DIGEST_LENGTH], const BIGNUM *N, const BIGNUM *g, const char *username, const char *password, const unsigned char *salt, size_t salt_len, const BIGNUM *A, const BIGNUM *B, const BIGNUM *a ) { SHA256_CTX ctx; unsigned char HN[SHA256_DIGEST_LENGTH], Hg[SHA256_DIGEST_LENGTH], HNxorg[SHA256_DIGEST_LENGTH]; unsigned char HI[SHA256_DIGEST_LENGTH]; /* H(N) xor H(g) */ hash_bn_pad(HN, N); hash_bn_pad(Hg, g); for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) HNxorg[i] = HN[i] ^ Hg[i]; /* H(I) */ SHA256((const unsigned char *)username, strlen(username), HI); /* x = H(s || H(I ":" P)) */ unsigned char inner_hash[SHA256_DIGEST_LENGTH]; SHA256_Init(&ctx); SHA256_Update(&ctx, (const unsigned char *)username, strlen(username)); SHA256_Update(&ctx, (const unsigned char *)":", 1); SHA256_Update(&ctx, (const unsigned char *)password, strlen(password)); SHA256_Final(inner_hash, &ctx); unsigned char x_hash[SHA256_DIGEST_LENGTH]; SHA256_Init(&ctx); SHA256_Update(&ctx, salt, salt_len); SHA256_Update(&ctx, inner_hash, SHA256_DIGEST_LENGTH); SHA256_Final(x_hash, &ctx); BIGNUM *x = BN_bin2bn(x_hash, SHA256_DIGEST_LENGTH, NULL); /* k = H(N || g) */ unsigned char k_hash[SHA256_DIGEST_LENGTH]; SHA256_Init(&ctx); unsigned char bufN[256], bufg[256]; memset(bufN, 0, 256); BN_bn2bin(N, bufN + (256 - BN_num_bytes(N))); memset(bufg, 0, 256); BN_bn2bin(g, bufg + (256 - BN_num_bytes(g))); SHA256_Update(&ctx, bufN, 256); SHA256_Update(&ctx, bufg, 256); SHA256_Final(k_hash, &ctx); BIGNUM *k = BN_bin2bn(k_hash, SHA256_DIGEST_LENGTH, NULL); /* u = H(A || B) */ unsigned char bufA[256], bufB[256], u_hash[SHA256_DIGEST_LENGTH]; memset(bufA, 0, 256); BN_bn2bin(A, bufA + (256 - BN_num_bytes(A))); memset(bufB, 0, 256); BN_bn2bin(B, bufB + (256 - BN_num_bytes(B))); SHA256_Init(&ctx); SHA256_Update(&ctx, bufA, 256); SHA256_Update(&ctx, bufB, 256); SHA256_Final(u_hash, &ctx); BIGNUM *u = BN_bin2bn(u_hash, SHA256_DIGEST_LENGTH, NULL); /* S = (B - k * g^x)^(a + u * x) mod N */ BN_CTX *bn_ctx = BN_CTX_new(); BIGNUM *gx = BN_new(), *kgx = BN_new(), *B_sub = BN_new(); BIGNUM *ux = BN_new(), *exp = BN_new(), *S = BN_new(); BN_mod_exp(gx, g, x, N, bn_ctx); /* g^x mod N */ BN_mod_mul(kgx, k, gx, N, bn_ctx); /* k * g^x mod N */ BN_mod_sub(B_sub, B, kgx, N, bn_ctx); /* B - k*g^x mod N */ BN_mul(ux, u, x, bn_ctx); /* u*x */ BN_add(exp, a, ux); /* a + u*x */ BN_mod_exp(S, B_sub, exp, N, bn_ctx); /* S */ /* K = H(S) */ unsigned char S_bytes[256]; memset(S_bytes, 0, 256); BN_bn2bin(S, S_bytes + (256 - BN_num_bytes(S))); unsigned char K[SHA256_DIGEST_LENGTH]; SHA256(S_bytes, 256, K); /* Compute M1 */ SHA256_Init(&ctx); SHA256_Update(&ctx, HNxorg, SHA256_DIGEST_LENGTH); SHA256_Update(&ctx, HI, SHA256_DIGEST_LENGTH); SHA256_Update(&ctx, salt, salt_len); SHA256_Update(&ctx, bufA, 256); SHA256_Update(&ctx, bufB, 256); SHA256_Update(&ctx, K, SHA256_DIGEST_LENGTH); SHA256_Final(M1, &ctx); /* Cleanup */ BN_free(x); BN_free(k); BN_free(u); BN_free(gx); BN_free(kgx); BN_free(B_sub); BN_free(ux); BN_free(exp); BN_free(S); BN_CTX_free(bn_ctx); } EAppleAuthStatus CAppleAuth::Submit2FA( const char *szCode ) { } CUtlString CAppleAuth::EncryptPassword( const char *szPassword, CUtlString szSalt, uint32_t uIters, EPasswordType eType ) { unsigned char hashedPassword[SHA256_DIGEST_LENGTH]; CUtlString szHashedPasswordHex = ""; unsigned char encryptedPassword[32]; CUtlString szEncryptedPasswordHex = ""; unsigned char salt[32]; int iSaltLen = base64_decode(szSalt, salt); if ( eType == PASSWORD_TYPE_S2K ) { for ( int i = 0; i < SHA256_DIGEST_LENGTH; i++ ) { szHashedPasswordHex.AppendTail(CUtlString("%02x", hashedPassword[i])); } PKCS5_PBKDF2_HMAC(szHashedPasswordHex, szHashedPasswordHex.GetLenght(), salt, iSaltLen, uIters, EVP_sha256(), 32, encryptedPassword); }; for ( int i = 0; i < SHA256_DIGEST_LENGTH; i++ ) { szEncryptedPasswordHex.AppendTail(CUtlString("%02x", encryptedPassword[i])); } V_printf("%s\n",szEncryptedPasswordHex.GetString()); return szEncryptedPasswordHex; } static CAppleAuth s_appleAuth; EXPOSE_INTERFACE_GLOBALVAR(CAppleAuth, IAppleAuth, APPLE_AUTH_INTERFACE_VERSION, s_appleAuth);