/////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// #include "AuthService.h" #include "../common/gsXML.h" #include "../common/gsAvailable.h" #include "../md5.h" #pragma warning(disable: 4267) /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// #define WS_AUTHSERVICE_LOGINPROFILE "LoginProfile" #define WS_AUTHSERVICE_LOGINUNIQUE "LoginUniqueNick" #define WS_AUTHSERVICE_LOGINREMOTEAUTH "LoginRemoteAuth" #define WS_AUTHSERVICE_LOGINPS3CERT "LoginPs3Cert" #define WS_AUTHSERVICE_PROTOVERSION 1 #define WS_AUTHSERVICE_NAMESPACE "ns1" #define WS_AUTHSERVICE_LOGINPROFILE_SOAP "SOAPAction: \"http://gamespy.net/AuthService/LoginProfile\"" #define WS_AUTHSERVICE_LOGINUNIQUE_SOAP "SOAPAction: \"http://gamespy.net/AuthService/LoginUniqueNick\"" #define WS_AUTHSERVICE_LOGINREMOTEAUTH_SOAP "SOAPAction: \"http://gamespy.net/AuthService/LoginRemoteAuth\"" #define WS_AUTHSERVICE_LOGINPS3CERT_SOAP "SOAPAction: \"http://gamespy.net/AuthService/LoginPs3Cert\"" #define WS_AUTHSERVICE_NAMESPACE_COUNT 1 const char * WS_AUTHSERVICE_NAMESPACES[WS_AUTHSERVICE_NAMESPACE_COUNT] = { WS_AUTHSERVICE_NAMESPACE "=\"http://gamespy.net/AuthService/\"" }; const char WS_AUTHSERVICE_SIGNATURE_KEY[] = "BF05D63E93751AD4A59A4A7389CF0BE8" "A22CCDEEA1E7F12C062D6E194472EFDA" "5184CCECEB4FBADF5EB1D7ABFE911814" "53972AA971F624AF9BA8F0F82E2869FB" "7D44BDE8D56EE50977898F3FEE758696" "22C4981F07506248BD3D092E8EA05C12" "B2FA37881176084C8F8B8756C4722CDC" "57D2AD28ACD3AD85934FB48D6B2D2027"; /* "D589F4433FAB2855F85A4EB40E6311F0" "284C7882A72380938FD0C55CC1D65F7C" "6EE79EDEF06C1AE5EDE139BDBBAB4219" "B7D2A3F0AC3D3B23F59F580E0E89B9EC" "787F2DD5A49788C633D5D3CE79438934" "861EA68AE545D5BBCAAAD917CE9F5C7C" "7D1452D9214F989861A7511097C35E60" "A7273DECEA71CB5F8251653B26ACE781";*/ const char WS_AUTHSERVICE_SIGNATURE_EXP[] = "010001"; // This is declared as an extern so it can be overridden when testing #define WS_LOGIN_SERVICE_URL_FORMAT "https://%s.auth.pubsvs." GSI_DOMAIN_NAME "/AuthService/AuthService.asmx" char wsAuthServiceURL[WS_LOGIN_MAX_URL_LEN] = ""; typedef struct WSIRequestData { union { WSLoginCallback mLoginCallback; WSLoginPs3CertCallback mLoginPs3CertCallback; } mUserCallback; //void * mUserCallback; void * mUserData; } WSIRequestData; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// static void wsiLoginEncryptPassword(const gsi_char * password, gsi_u8 ciphertext[GS_CRYPT_RSA_BYTE_SIZE]); static gsi_bool wsiServiceAvailable(); /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Checks to make sure the availability check has been performed prior // to using any AuthService Login, and sets the service URL if it has static gsi_bool wsiServiceAvailable() { if (__GSIACResult == GSIACAvailable) { if (wsAuthServiceURL[0] == '\0') snprintf(wsAuthServiceURL, WS_LOGIN_MAX_URL_LEN, WS_LOGIN_SERVICE_URL_FORMAT, __GSIACGamename); return gsi_true; } else return gsi_false; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// static void wsiLoginProfileCallback(GHTTPResult theResult, GSXmlStreamWriter theRequestXml, GSXmlStreamReader theResponseXml, void * theRequestData) { GHTTPResult translatedResult = GHTTPSuccess; WSIRequestData * requestData = (WSIRequestData*)theRequestData; WSLoginResponse response; GSLoginCertificate * cert = &response.mCertificate; // for convenience // initialize local variables cert->mIsValid = gsi_false; memset(&response, 0, sizeof(response)); if (theResult == GHTTPSuccess) { // try to parse the soap if (gsi_is_false(gsXmlMoveToStart(theResponseXml)) || gsi_is_false(gsXmlMoveToNext(theResponseXml, "LoginProfileResult"))) { response.mLoginResult = WSLogin_ParseError; } else { // prepare response structure if (gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "responseCode", (int*)&response.mResponseCode))) { // could not parse login response code response.mLoginResult = WSLogin_ParseError; } else if (response.mResponseCode != WSLogin_Success) { // server reported an error into reponseCode response.mLoginResult = WSLogin_ServerError; } else if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "certificate")) || gsi_is_false(wsLoginCertReadXML(cert, theResponseXml)) || gsi_is_false(gsXmlMoveToParent(theResponseXml)) || gsi_is_false(gsXmlReadChildAsLargeInt(theResponseXml, "peerkeyprivate", &response.mPrivateData.mPeerPrivateKey.exponent)) ) { response.mLoginResult = WSLogin_ParseError; } else { MD5_CTX md5; // peer privatekey modulus is same as peer public key modulus memcpy(&response.mPrivateData.mPeerPrivateKey.modulus, &cert->mPeerPublicKey.modulus, sizeof(cert->mPeerPublicKey.modulus)); // hash the private key MD5Init(&md5); //gsLargeIntAddToMD5(&response.mPrivateData.mPeerPrivateKey.modulus, &md5); gsLargeIntAddToMD5(&response.mPrivateData.mPeerPrivateKey.exponent, &md5); MD5Final((unsigned char*)response.mPrivateData.mKeyHash, &md5); // verify certificate cert->mIsValid = wsLoginCertIsValid(cert); if (gsi_is_false(cert->mIsValid)) { response.mLoginResult = WSLogin_InvalidCertificate; } } } } else { response.mLoginResult = WSLogin_HttpError; } // trigger the user callback if (requestData->mUserCallback.mLoginCallback != NULL) { WSLoginCallback userCallback = (WSLoginCallback)(requestData->mUserCallback.mLoginCallback); (userCallback)(translatedResult, &response, requestData->mUserData); } gsifree(requestData); GSI_UNUSED(theRequestXml); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// gsi_u32 wsLoginProfile(int partnerCode, int namespaceId, const gsi_char * profileNick, const gsi_char * email, const gsi_char * password, const gsi_char * cdkey, WSLoginCallback callback, void * userData) { GSXmlStreamWriter writer; WSIRequestData * requestData = NULL; gsi_u8 encryptedPassword[GS_CRYPT_RSA_BYTE_SIZE]; if (!wsiServiceAvailable()) return WSLogin_NoAvailabilityCheck; GS_ASSERT(partnerCode >= 0); GS_ASSERT(profileNick != NULL); GS_ASSERT(email != NULL); GS_ASSERT(password != NULL); if (_tcslen(profileNick) >= WS_LOGIN_NICK_LEN) return WSLogin_InvalidParameters; if (_tcslen(email) >= WS_LOGIN_EMAIL_LEN) return WSLogin_InvalidParameters; if (_tcslen(password) >= WS_LOGIN_PASSWORD_LEN) return WSLogin_InvalidParameters; if (cdkey != NULL && (_tcslen(cdkey) > WS_LOGIN_CDKEY_LEN)) return WSLogin_InvalidParameters; // make a copy of the request callback and user param requestData = (WSIRequestData*)gsimalloc(sizeof(WSIRequestData)); if (requestData == NULL) return WSLogin_OutOfMemory; requestData->mUserCallback.mLoginCallback = callback; requestData->mUserData = userData; // encrypt the password (includes safety padding and hash) wsiLoginEncryptPassword(password, encryptedPassword); writer = gsXmlCreateStreamWriter(WS_AUTHSERVICE_NAMESPACES, WS_AUTHSERVICE_NAMESPACE_COUNT); if (writer != NULL) { GSSoapTask * aTask = NULL; if (gsi_is_false(gsXmlWriteOpenTag (writer, WS_AUTHSERVICE_NAMESPACE, WS_AUTHSERVICE_LOGINPROFILE)) || gsi_is_false(gsXmlWriteIntElement (writer, WS_AUTHSERVICE_NAMESPACE, "version", WS_AUTHSERVICE_PROTOVERSION)) || gsi_is_false(gsXmlWriteIntElement (writer, WS_AUTHSERVICE_NAMESPACE, "partnercode", (gsi_u32)partnerCode)) || gsi_is_false(gsXmlWriteIntElement (writer, WS_AUTHSERVICE_NAMESPACE, "namespaceid", (gsi_u32)namespaceId)) || gsi_is_false(gsXmlWriteAsciiStringElement(writer, WS_AUTHSERVICE_NAMESPACE, "email", email)) || gsi_is_false(gsXmlWriteAsciiStringElement(writer, WS_AUTHSERVICE_NAMESPACE, "profilenick", profileNick)) || gsi_is_false(gsXmlWriteOpenTag (writer, WS_AUTHSERVICE_NAMESPACE, "password")) || gsi_is_false(gsXmlWriteHexBinaryElement(writer, WS_AUTHSERVICE_NAMESPACE, "Value", encryptedPassword, GS_CRYPT_RSA_BYTE_SIZE)) || gsi_is_false(gsXmlWriteCloseTag (writer, WS_AUTHSERVICE_NAMESPACE, "password")) || gsi_is_false(gsXmlWriteCloseTag (writer, WS_AUTHSERVICE_NAMESPACE, WS_AUTHSERVICE_LOGINPROFILE)) || gsi_is_false(gsXmlCloseWriter (writer)) ) { gsXmlFreeWriter(writer); return WSLogin_OutOfMemory; } aTask = gsiExecuteSoap(wsAuthServiceURL, WS_AUTHSERVICE_LOGINPROFILE_SOAP, writer, wsiLoginProfileCallback, (void*)requestData); if (aTask == NULL) { gsXmlFreeWriter(writer); gsifree(requestData); return WSLogin_OutOfMemory; } } return 0; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// static void wsLoginUniqueCallback(GHTTPResult theResult, GSXmlStreamWriter theRequestXml, GSXmlStreamReader theResponseXml, void * theRequestData) { GHTTPResult translatedResult = theResult; WSIRequestData * requestData = (WSIRequestData*)theRequestData; WSLoginResponse response; GSLoginCertificate * cert = &response.mCertificate; // for convenience cert->mIsValid = gsi_false; memset(&response, 0, sizeof(response)); if (theResult == GHTTPSuccess) { // try to parse the soap if (gsi_is_false(gsXmlMoveToStart(theResponseXml)) || gsi_is_false(gsXmlMoveToNext(theResponseXml, "LoginUniqueNickResult"))) { response.mLoginResult = WSLogin_ParseError; } else { // prepare response structure if (gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "responseCode", (int*)&response.mResponseCode))) { // could not parse login response code response.mLoginResult = WSLogin_ParseError; } else if (response.mResponseCode != WSLogin_Success) { // server reported an error into reponseCode response.mLoginResult = WSLogin_ServerError; } else if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "certificate")) || gsi_is_false(wsLoginCertReadXML(cert, theResponseXml)) || gsi_is_false(gsXmlMoveToParent(theResponseXml)) || gsi_is_false(gsXmlReadChildAsLargeInt(theResponseXml, "peerkeyprivate", &response.mPrivateData.mPeerPrivateKey.exponent)) ) { response.mLoginResult = WSLogin_ParseError; } else { MD5_CTX md5; // peer privatekey modulus is same as peer public key modulus memcpy(&response.mPrivateData.mPeerPrivateKey.modulus, &cert->mPeerPublicKey.modulus, sizeof(cert->mPeerPublicKey.modulus)); // hash the private key // -- we use the has like a password for simple authentication MD5Init(&md5); //gsLargeIntAddToMD5(&response.mPrivateData.mPeerPrivateKey.modulus, &md5); gsLargeIntAddToMD5(&response.mPrivateData.mPeerPrivateKey.exponent, &md5); MD5Final((unsigned char*)response.mPrivateData.mKeyHash, &md5); // verify certificate cert->mIsValid = wsLoginCertIsValid(cert); if (gsi_is_false(cert->mIsValid)) { response.mLoginResult = WSLogin_InvalidCertificate; } } } } else if (theResult == GHTTPRequestCancelled) { response.mLoginResult = WSLogin_Cancelled; } else { response.mLoginResult = WSLogin_HttpError; } // trigger the user callback if (requestData->mUserCallback.mLoginCallback != NULL) { WSLoginCallback userCallback = (WSLoginCallback)(requestData->mUserCallback.mLoginCallback); (userCallback)(translatedResult, &response, requestData->mUserData); } gsifree(requestData); GSI_UNUSED(theRequestXml); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// //gsi_u32 wsLoginUnique(WSLoginUniqueRequest * request, WSLoginCallback callback) gsi_u32 wsLoginUnique(int partnerCode, int namespaceId, const gsi_char * uniqueNick, const gsi_char * password, const gsi_char * cdkey, WSLoginCallback userCallback, void * userData) { GSXmlStreamWriter writer; WSIRequestData * requestData = NULL; gsi_u8 encryptedPassword[GS_CRYPT_RSA_BYTE_SIZE]; if (!wsiServiceAvailable()) return WSLogin_NoAvailabilityCheck; GS_ASSERT(partnerCode >= 0); GS_ASSERT(uniqueNick != NULL); GS_ASSERT(password != NULL); if (_tcslen(uniqueNick) >= WS_LOGIN_UNIQUENICK_LEN) return WSLogin_InvalidParameters; if (_tcslen(password) >= WS_LOGIN_PASSWORD_LEN) return WSLogin_InvalidParameters; if (cdkey != NULL && (_tcslen(cdkey) >= WS_LOGIN_CDKEY_LEN)) return WSLogin_InvalidParameters; // allocate the request values requestData = (WSIRequestData*)gsimalloc(sizeof(WSIRequestData)); if (requestData == NULL) return WSLogin_OutOfMemory; requestData->mUserCallback.mLoginCallback = userCallback; requestData->mUserData = userData; // encrypt the password (includes safety padding and hash) wsiLoginEncryptPassword(password, encryptedPassword); // create the xml request writer = gsXmlCreateStreamWriter(WS_AUTHSERVICE_NAMESPACES, WS_AUTHSERVICE_NAMESPACE_COUNT); if (writer != NULL) { GSSoapTask * aTask = NULL; if (gsi_is_false(gsXmlWriteOpenTag (writer, WS_AUTHSERVICE_NAMESPACE, WS_AUTHSERVICE_LOGINUNIQUE)) || gsi_is_false(gsXmlWriteIntElement (writer, WS_AUTHSERVICE_NAMESPACE, "version", WS_AUTHSERVICE_PROTOVERSION)) || gsi_is_false(gsXmlWriteIntElement (writer, WS_AUTHSERVICE_NAMESPACE, "partnercode", (gsi_u32)partnerCode)) || gsi_is_false(gsXmlWriteIntElement (writer, WS_AUTHSERVICE_NAMESPACE, "namespaceid", (gsi_u32)namespaceId)) || gsi_is_false(gsXmlWriteAsciiStringElement(writer, WS_AUTHSERVICE_NAMESPACE, "uniquenick", uniqueNick)) || gsi_is_false(gsXmlWriteOpenTag (writer, WS_AUTHSERVICE_NAMESPACE, "password")) || gsi_is_false(gsXmlWriteHexBinaryElement(writer, WS_AUTHSERVICE_NAMESPACE, "Value", encryptedPassword, GS_CRYPT_RSA_BYTE_SIZE)) || gsi_is_false(gsXmlWriteCloseTag (writer, WS_AUTHSERVICE_NAMESPACE, "password")) || gsi_is_false(gsXmlWriteCloseTag (writer, WS_AUTHSERVICE_NAMESPACE, WS_AUTHSERVICE_LOGINUNIQUE)) || gsi_is_false(gsXmlCloseWriter (writer)) ) { gsXmlFreeWriter(writer); return WSLogin_OutOfMemory; } aTask = gsiExecuteSoap(wsAuthServiceURL, WS_AUTHSERVICE_LOGINUNIQUE_SOAP, writer, wsLoginUniqueCallback, (void*)requestData); if (aTask == NULL) { gsXmlFreeWriter(writer); gsifree(requestData); return WSLogin_OutOfMemory; } } return 0; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// static void wsLoginRemoteAuthCallback(GHTTPResult theResult, GSXmlStreamWriter theRequestXml, GSXmlStreamReader theResponseXml, void * theRequestData) { GHTTPResult translatedResult = theResult; WSIRequestData * requestData = (WSIRequestData*)theRequestData; WSLoginResponse response; GSLoginCertificate * cert = &response.mCertificate; // for convenience cert->mIsValid = gsi_false; memset(&response, 0, sizeof(response)); if (theResult == GHTTPSuccess) { // try to parse the soap if (gsi_is_false(gsXmlMoveToStart(theResponseXml)) || gsi_is_false(gsXmlMoveToNext(theResponseXml, "LoginRemoteAuthResult"))) { response.mLoginResult = WSLogin_ParseError; } else { // prepare response structure if (gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "responseCode", (int*)&response.mResponseCode))) { // could not parse login response code response.mLoginResult = WSLogin_ParseError; } else if (response.mResponseCode != WSLogin_Success) { // server reported an error into reponseCode response.mLoginResult = WSLogin_ServerError; } else if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "certificate")) || gsi_is_false(wsLoginCertReadXML(cert, theResponseXml)) || gsi_is_false(gsXmlMoveToParent(theResponseXml)) || gsi_is_false(gsXmlReadChildAsLargeInt(theResponseXml, "peerkeyprivate", &response.mPrivateData.mPeerPrivateKey.exponent)) ) { response.mLoginResult = WSLogin_ParseError; } else { MD5_CTX md5; // peer privatekey modulus is same as peer public key modulus memcpy(&response.mPrivateData.mPeerPrivateKey.modulus, &cert->mPeerPublicKey.modulus, sizeof(cert->mPeerPublicKey.modulus)); // hash the private key // -- we use the has like a password for simple authentication MD5Init(&md5); //gsLargeIntAddToMD5(&response.mPrivateData.mPeerPrivateKey.modulus, &md5); gsLargeIntAddToMD5(&response.mPrivateData.mPeerPrivateKey.exponent, &md5); MD5Final((unsigned char*)response.mPrivateData.mKeyHash, &md5); // verify certificate cert->mIsValid = wsLoginCertIsValid(cert); if (gsi_is_false(cert->mIsValid)) { response.mLoginResult = WSLogin_InvalidCertificate; } } } } else if (theResult == GHTTPRequestCancelled) { response.mLoginResult = WSLogin_Cancelled; } else { response.mLoginResult = WSLogin_HttpError; } // trigger the user callback if (requestData->mUserCallback.mLoginCallback != NULL) { WSLoginCallback userCallback = (WSLoginCallback)(requestData->mUserCallback.mLoginCallback); (userCallback)(translatedResult, &response, requestData->mUserData); } gsifree(requestData); GSI_UNUSED(theRequestXml); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// gsi_u32 wsLoginRemoteAuth(int partnerCode, int namespaceId, const gsi_char authtoken[WS_LOGIN_AUTHTOKEN_LEN], const gsi_char partnerChallenge[WS_LOGIN_PARTNERCHALLENGE_LEN], WSLoginCallback userCallback, void * userData) { GSXmlStreamWriter writer; WSIRequestData * requestData = NULL; //gsi_u8 encryptedChallenge[GS_CRYPT_RSA_BYTE_SIZE]; if (!wsiServiceAvailable()) return WSLogin_NoAvailabilityCheck; GS_ASSERT(partnerCode >= 0); // allocate the request values requestData = (WSIRequestData*)gsimalloc(sizeof(WSIRequestData)); if (requestData == NULL) return WSLogin_OutOfMemory; requestData->mUserCallback.mLoginCallback = userCallback; requestData->mUserData = userData; // encrypt the password (includes safety padding and hash) //wsiLoginEncryptPassword(partnerChallenge, encryptedChallenge); // create the xml request writer = gsXmlCreateStreamWriter(WS_AUTHSERVICE_NAMESPACES, WS_AUTHSERVICE_NAMESPACE_COUNT); if (writer != NULL) { GSSoapTask * aTask = NULL; if (gsi_is_false(gsXmlWriteOpenTag (writer, WS_AUTHSERVICE_NAMESPACE, WS_AUTHSERVICE_LOGINREMOTEAUTH)) || gsi_is_false(gsXmlWriteIntElement (writer, WS_AUTHSERVICE_NAMESPACE, "version", WS_AUTHSERVICE_PROTOVERSION)) || gsi_is_false(gsXmlWriteIntElement (writer, WS_AUTHSERVICE_NAMESPACE, "partnercode", (gsi_u32)partnerCode)) || gsi_is_false(gsXmlWriteIntElement (writer, WS_AUTHSERVICE_NAMESPACE, "namespaceid", (gsi_u32)namespaceId)) || gsi_is_false(gsXmlWriteTStringElement(writer, WS_AUTHSERVICE_NAMESPACE, "authtoken", authtoken)) || gsi_is_false(gsXmlWriteTStringElement(writer, WS_AUTHSERVICE_NAMESPACE, "challenge", partnerChallenge)) || //gsi_is_false(gsXmlWriteOpenTag (writer, WS_AUTHSERVICE_NAMESPACE, "challenge")) || //gsi_is_false(gsXmlWriteHexBinaryElement(writer, WS_AUTHSERVICE_NAMESPACE, "Value", encryptedChallenge, GS_CRYPT_RSA_BYTE_SIZE)) || //gsi_is_false(gsXmlWriteCloseTag (writer, WS_AUTHSERVICE_NAMESPACE, "challenge")) || gsi_is_false(gsXmlWriteCloseTag (writer, WS_AUTHSERVICE_NAMESPACE, WS_AUTHSERVICE_LOGINREMOTEAUTH)) || gsi_is_false(gsXmlCloseWriter (writer)) ) { gsXmlFreeWriter(writer); return WSLogin_OutOfMemory; } aTask = gsiExecuteSoap(wsAuthServiceURL, WS_AUTHSERVICE_LOGINREMOTEAUTH_SOAP, writer, wsLoginRemoteAuthCallback, (void*)requestData); if (aTask == NULL) { gsXmlFreeWriter(writer); gsifree(requestData); return WSLogin_OutOfMemory; } } return 0; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// static void wsLoginPs3CertCallback(GHTTPResult theResult, GSXmlStreamWriter theRequestXml, GSXmlStreamReader theResponseXml, void * theRequestData) { GHTTPResult translatedResult = theResult; WSIRequestData * requestData = (WSIRequestData*)theRequestData; WSLoginPs3CertResponse response; memset(&response, 0, sizeof(response)); if (theResult == GHTTPSuccess) { // try to parse the soap if (gsi_is_false(gsXmlMoveToStart(theResponseXml)) || gsi_is_false(gsXmlMoveToNext(theResponseXml, "LoginPs3CertResult"))) { response.mLoginResult = WSLogin_ParseError; } else { // prepare response structure if (gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "responseCode", (int*)&response.mResponseCode))) { // could not parse login response code response.mLoginResult = WSLogin_ParseError; } else if (response.mResponseCode != WSLogin_Success) { // server reported an error into reponseCode response.mLoginResult = WSLogin_ServerError; } else { const char * tokenStr = NULL; const char * challengeStr = NULL; int tokenLen = 0; int challengeLen = 0; // Check length of token+challenge, then read into memory if ( //gsi_is_false(gsXmlReadChildAsBase64Binary(theResponseXml, "authToken", NULL, &tokenLen)) || //gsi_is_false(gsXmlReadChildAsBase64Binary(theResponseXml, "partnerChallenge", NULL, &challengeLen)) || //(tokenLen > WS_LOGIN_AUTHTOKEN_LEN || challengeLen > WS_LOGIN_PARTNERCHALLENGE_LEN) || gsi_is_false(gsXmlReadChildAsString(theResponseXml, "authToken", &tokenStr, &tokenLen)) || gsi_is_false(gsXmlReadChildAsString(theResponseXml, "partnerChallenge", &challengeStr, &challengeLen)) || (tokenLen >= WS_LOGIN_AUTHTOKEN_LEN || challengeLen >= WS_LOGIN_PARTNERCHALLENGE_LEN) ) { response.mLoginResult = WSLogin_ParseError; } else { memcpy(response.mRemoteAuthToken, tokenStr, (gsi_u32)tokenLen); memcpy(response.mPartnerChallenge, challengeStr, (gsi_u32)challengeLen); response.mRemoteAuthToken[tokenLen] = '\0'; response.mPartnerChallenge[challengeLen] = '\0'; } } } } else if (theResult == GHTTPRequestCancelled) { response.mLoginResult = WSLogin_Cancelled; } else { response.mLoginResult = WSLogin_HttpError; } // trigger the user callback if (requestData->mUserCallback.mLoginPs3CertCallback != NULL) { WSLoginPs3CertCallback userCallback = (WSLoginPs3CertCallback)(requestData->mUserCallback.mLoginPs3CertCallback); (userCallback)(translatedResult, &response, requestData->mUserData); } gsifree(requestData); GSI_UNUSED(theRequestXml); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// //gsi_u32 wsLoginUnique(WSLoginUniqueRequest * request, WSLoginCallback callback) gsi_u32 wsLoginPs3Cert(int gameId, int partnerCode, int namespaceId, const gsi_u8 * ps3cert, int certLen, WSLoginPs3CertCallback userCallback, void * userData) { GSXmlStreamWriter writer; WSIRequestData * requestData = NULL; if (!wsiServiceAvailable()) return WSLogin_NoAvailabilityCheck; GS_ASSERT(partnerCode >= 0); GS_ASSERT(ps3cert != NULL); // Version check, todo: use something better than length //if (certLen != 248) // return WSLogin_InvalidParameters; // allocate the request values requestData = (WSIRequestData*)gsimalloc(sizeof(WSIRequestData)); if (requestData == NULL) return WSLogin_OutOfMemory; requestData->mUserCallback.mLoginPs3CertCallback = userCallback; requestData->mUserData = userData; // create the xml request writer = gsXmlCreateStreamWriter(WS_AUTHSERVICE_NAMESPACES, WS_AUTHSERVICE_NAMESPACE_COUNT); if (writer != NULL) { GSSoapTask * aTask = NULL; if (gsi_is_false(gsXmlWriteOpenTag (writer, WS_AUTHSERVICE_NAMESPACE, WS_AUTHSERVICE_LOGINPS3CERT)) || gsi_is_false(gsXmlWriteIntElement (writer, WS_AUTHSERVICE_NAMESPACE, "version", WS_AUTHSERVICE_PROTOVERSION)) || gsi_is_false(gsXmlWriteIntElement (writer, WS_AUTHSERVICE_NAMESPACE, "gameid", (gsi_u32)gameId)) || gsi_is_false(gsXmlWriteIntElement (writer, WS_AUTHSERVICE_NAMESPACE, "partnercode", (gsi_u32)partnerCode)) || gsi_is_false(gsXmlWriteIntElement (writer, WS_AUTHSERVICE_NAMESPACE, "namespaceid", (gsi_u32)namespaceId)) || gsi_is_false(gsXmlWriteOpenTag (writer, WS_AUTHSERVICE_NAMESPACE, "npticket")) || gsi_is_false(gsXmlWriteBase64BinaryElement(writer, WS_AUTHSERVICE_NAMESPACE, "Value", ps3cert, certLen)) || gsi_is_false(gsXmlWriteCloseTag (writer, WS_AUTHSERVICE_NAMESPACE, "npticket")) || gsi_is_false(gsXmlWriteCloseTag (writer, WS_AUTHSERVICE_NAMESPACE, WS_AUTHSERVICE_LOGINPS3CERT)) || gsi_is_false(gsXmlCloseWriter (writer)) ) { gsXmlFreeWriter(writer); return WSLogin_OutOfMemory; } aTask = gsiExecuteSoap(wsAuthServiceURL, WS_AUTHSERVICE_LOGINPS3CERT_SOAP, writer, wsLoginPs3CertCallback, (void*)requestData); if (aTask == NULL) { gsXmlFreeWriter(writer); gsifree(requestData); return WSLogin_OutOfMemory; } } return 0; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// static void wsiLoginEncryptPassword(const gsi_char * password, gsi_u8 ciphertext[GS_CRYPT_RSA_BYTE_SIZE]) { gsCryptRSAKey sigkeypub; #ifdef GSI_UNICODE char password_A[WS_LOGIN_PASSWORD_LEN]; // strip password into ascii to encrypt UCS2ToAsciiString(password, password_A); #endif gsLargeIntSetFromHexString(&sigkeypub.modulus, WS_AUTHSERVICE_SIGNATURE_KEY); gsLargeIntSetFromHexString(&sigkeypub.exponent, WS_AUTHSERVICE_SIGNATURE_EXP); #ifdef GSI_UNICODE gsCryptRSAEncryptBuffer(&sigkeypub, (const gsi_u8*)password_A, _tcslen(password), ciphertext); #else gsCryptRSAEncryptBuffer(&sigkeypub, (const gsi_u8*)password, strlen(password), ciphertext); #endif } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// static gsi_u32 wsiMakeLittleEndian32(gsi_u32 num) { #if defined(GSI_BIG_ENDIAN) num = gsiByteOrderSwap32(num); #endif return num; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// gsi_bool wsLoginCertIsValid(const GSLoginCertificate * cert) { // Verify the signature gsCryptRSAKey sigkeypub; MD5_CTX md5; gsi_u8 hash[16]; gsi_i32 cryptResult = 0; gsi_u32 temp; // hash certificate data MD5Init(&md5); temp = wsiMakeLittleEndian32(cert->mLength); MD5Update(&md5, (unsigned char*)&temp, 4); temp = wsiMakeLittleEndian32(cert->mVersion); MD5Update(&md5, (unsigned char*)&temp, 4); temp = wsiMakeLittleEndian32(cert->mPartnerCode); MD5Update(&md5, (unsigned char*)&temp, 4); temp = wsiMakeLittleEndian32(cert->mNamespaceId); MD5Update(&md5, (unsigned char*)&temp, 4); temp = wsiMakeLittleEndian32(cert->mUserId); MD5Update(&md5, (unsigned char*)&temp, 4); temp = wsiMakeLittleEndian32(cert->mProfileId); MD5Update(&md5, (unsigned char*)&temp, 4); temp = wsiMakeLittleEndian32(cert->mExpireTime); MD5Update(&md5, (unsigned char*)&temp, 4); #if defined(GSI_UNICODE) { char profile_A[WS_LOGIN_NICK_LEN]; char uniquenick_A[WS_LOGIN_UNIQUENICK_LEN]; char keyhash_A[WS_LOGIN_KEYHASH_LEN]; UCS2ToAsciiString(cert->mProfileNick, profile_A); UCS2ToAsciiString(cert->mUniqueNick, uniquenick_A); UCS2ToAsciiString(cert->mCdKeyHash, keyhash_A); MD5Update(&md5, (unsigned char*)profile_A, strlen(profile_A)); //FIX for unicode MD5Update(&md5, (unsigned char*)uniquenick_A, strlen(uniquenick_A)); //FIX for unicode MD5Update(&md5, (unsigned char*)keyhash_A, strlen(keyhash_A)); //FIX for unicode } #else MD5Update(&md5, (unsigned char*)&cert->mProfileNick, strlen(cert->mProfileNick)); MD5Update(&md5, (unsigned char*)&cert->mUniqueNick, strlen(cert->mUniqueNick)); MD5Update(&md5, (unsigned char*)&cert->mCdKeyHash, strlen(cert->mCdKeyHash)); #endif // must be hashed in big endian byte order // skip leading zeroes gsLargeIntAddToMD5(&cert->mPeerPublicKey.modulus, &md5); gsLargeIntAddToMD5(&cert->mPeerPublicKey.exponent, &md5); MD5Update(&md5, (unsigned char*)&cert->mServerData, WS_LOGIN_SERVERDATA_LEN); MD5Final(hash, &md5); gsLargeIntSetFromHexString(&sigkeypub.modulus, WS_AUTHSERVICE_SIGNATURE_KEY); gsLargeIntSetFromHexString(&sigkeypub.exponent, WS_AUTHSERVICE_SIGNATURE_EXP); cryptResult = gsCryptRSAVerifySignedHash(&sigkeypub, hash, 16, cert->mSignature, WS_LOGIN_SIGNATURE_LEN); if (cryptResult == 0) return gsi_true; else return gsi_false; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Utility to write a certificate in binary form, does it really belong in this file? #define WRITE_NETWORK_INT(a) { \ intNB = (gsi_i32)htonl(a); \ if(lenoutSoFar + sizeof(intNB) > maxlen) \ return gsi_false; \ memcpy(bufout+lenoutSoFar, &intNB, sizeof(intNB)); \ lenoutSoFar += sizeof(intNB); } #define WRITE_NTS(a) { \ if(lenoutSoFar + _tcslen(a) > maxlen) \ return gsi_false; \ strcpy(bufout+lenoutSoFar, a); \ lenoutSoFar += _tcslen(a) + 1; } #define WRITE_BINARY(a,l) { \ if(lenoutSoFar + l > maxlen) \ return gsi_false; \ memcpy(bufout+lenoutSoFar, a, l); \ lenoutSoFar += l; } #define WRITE_REV_BINARY(a,l) { \ int i=(gsi_i32)l; \ const char * readPos = ((char*)a)+i-1; \ if (lenoutSoFar + l > maxlen) \ return gsi_false; \ while(i > 0) \ { \ *(bufout+lenoutSoFar) = *readPos; \ readPos--; lenoutSoFar++; i--; \ }; \ } gsi_bool wsLoginCertWriteBinary(const GSLoginCertificate * cert, char * bufout, unsigned int maxlen, unsigned int * lenout) { gsi_i32 intNB; // network byte order int gsi_i32 lenoutSoFar = 0; // tracks bytes written to bufout gsi_i32 lenTemp = 0; // for temp length of large ints WRITE_NETWORK_INT(cert->mLength); WRITE_NETWORK_INT(cert->mVersion); WRITE_NETWORK_INT(cert->mPartnerCode); WRITE_NETWORK_INT(cert->mNamespaceId); WRITE_NETWORK_INT(cert->mUserId); WRITE_NETWORK_INT(cert->mProfileId); WRITE_NETWORK_INT(cert->mExpireTime); #ifndef GSI_UNICODE WRITE_NTS(cert->mProfileNick); WRITE_NTS(cert->mUniqueNick); WRITE_NTS(cert->mCdKeyHash); #else if((lenoutSoFar + _tcslen(cert->mProfileNick) + _tcslen(cert->mUniqueNick) + _tcslen(cert->mCdKeyHash)) > maxlen) return gsi_false; // strip unicode to Ascii before writing this to the buffer lenoutSoFar += UCS2ToAsciiString(cert->mProfileNick, bufout+lenoutSoFar)+1; lenoutSoFar += UCS2ToAsciiString(cert->mUniqueNick, bufout+lenoutSoFar)+1; lenoutSoFar += UCS2ToAsciiString(cert->mCdKeyHash, bufout+lenoutSoFar)+1; #endif lenTemp = (gsi_i32)gsLargeIntGetByteLength(&cert->mPeerPublicKey.modulus); // size in bytes, no leading zeroes WRITE_NETWORK_INT(lenTemp); WRITE_REV_BINARY(cert->mPeerPublicKey.modulus.mData, (unsigned int)lenTemp); lenTemp = (gsi_i32)gsLargeIntGetByteLength(&cert->mPeerPublicKey.exponent); // size in bytes, no leading zeroes WRITE_NETWORK_INT(lenTemp); WRITE_REV_BINARY(cert->mPeerPublicKey.exponent.mData, (unsigned int)lenTemp); WRITE_NETWORK_INT(WS_LOGIN_SERVERDATA_LEN); WRITE_BINARY(cert->mServerData, (unsigned int)WS_LOGIN_SERVERDATA_LEN); WRITE_NETWORK_INT(GS_CRYPT_RSA_BYTE_SIZE); WRITE_BINARY(cert->mSignature, (unsigned int)WS_LOGIN_SERVERDATA_LEN); *lenout = (gsi_u32)lenoutSoFar; return gsi_true; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Utility to read the binary certificate // each read macro first clears the buffer (a) before writing #define READ_NTOH_INT(a) { \ gsi_i32 temp; \ memset(&a, 0, sizeof(a)); \ memcpy(&temp, bufin, sizeof(gsi_i32)); \ intHB = (gsi_u32)ntohl(temp); \ memcpy(&a, &intHB, sizeof(intHB)); \ if(lenoutSoFar + sizeof(intHB) > maxlen) \ return gsi_false; \ bufin += sizeof(gsi_u32); \ lenoutSoFar += sizeof(intHB); } #define READ_NTS(a,l) { \ memset(&a, 0, sizeof(a)); \ _tcsncpy(a, bufin, l); \ if(lenoutSoFar + _tcslen(a)+1 > maxlen) \ return gsi_false; \ bufin += _tcslen(a)+1; \ lenoutSoFar += _tcslen(a)+1; } #define READ_ASCII(a,l) { \ char temp[l]; \ memset(&a, 0, sizeof(a)); \ strncpy(temp, bufin, l); \ AsciiToUCS2String(temp, a); \ if(lenoutSoFar + _tcslen(a)+1 > maxlen) \ return gsi_false; \ bufin += _tcslen(a)+1; \ lenoutSoFar += _tcslen(a)+1; } #define READ_BINARY(a,l) { \ int index = 0; \ memset(&a, 0, sizeof(a)); \ if(lenoutSoFar + l > maxlen) \ return gsi_false; \ memcpy(a+index, bufin, l); \ lenoutSoFar += l; \ bufin += l; } #define READ_REV_BINARY_TO_INT(a,l) { \ int i=(gsi_i32)l; \ int index = 0; \ char temp[4]; \ const char * readPos = bufin+i-1; \ memset(&a, 0, sizeof(a)); \ if (lenoutSoFar + l > maxlen) \ return gsi_false; \ while(i > 0) \ { \ temp[index%4] = *readPos; \ if (index%4 == 3) \ memcpy(a+(index/4), temp, 4); \ else if (index == (gsi_i32)l-1) \ memcpy(a+(index/4), temp, l); \ readPos--; index++; lenoutSoFar++; i--; \ }; \ bufin += l; } gsi_bool wsLoginCertReadBinary(GSLoginCertificate * certOut, char * bufin, unsigned int maxlen) { gsi_u32 intHB; // host byte order int gsi_u32 lenoutSoFar = 0; // tracks bufin index to make sure we dont exceed the maxlen gsi_u32 lenTemp = 0; // for temp length of large ints READ_NTOH_INT(certOut->mLength); READ_NTOH_INT(certOut->mVersion); READ_NTOH_INT(certOut->mPartnerCode); READ_NTOH_INT(certOut->mNamespaceId); READ_NTOH_INT(certOut->mUserId); READ_NTOH_INT(certOut->mProfileId); READ_NTOH_INT(certOut->mExpireTime); #ifndef GSI_UNICODE READ_NTS(certOut->mProfileNick, WS_LOGIN_NICK_LEN); READ_NTS(certOut->mUniqueNick, WS_LOGIN_UNIQUENICK_LEN); READ_NTS(certOut->mCdKeyHash, WS_LOGIN_CDKEY_LEN); #else // parses ascii to unicode before writing into the buffer READ_ASCII(certOut->mProfileNick, WS_LOGIN_NICK_LEN); READ_ASCII(certOut->mUniqueNick, WS_LOGIN_UNIQUENICK_LEN); READ_ASCII(certOut->mCdKeyHash, WS_LOGIN_CDKEY_LEN); #endif READ_NTOH_INT(lenTemp); //size of the modulus data in bytes // now calculate the length in int's certOut->mPeerPublicKey.modulus.mLength = (lenTemp / GS_LARGEINT_DIGIT_SIZE_BYTES); if (lenTemp % GS_LARGEINT_DIGIT_SIZE_BYTES != 0) certOut->mPeerPublicKey.modulus.mLength++; READ_REV_BINARY_TO_INT(certOut->mPeerPublicKey.modulus.mData, lenTemp); READ_NTOH_INT(lenTemp); //size of the exponent data in bytes // now calculate the length in int's certOut->mPeerPublicKey.exponent.mLength = (lenTemp / GS_LARGEINT_DIGIT_SIZE_BYTES); if (lenTemp % GS_LARGEINT_DIGIT_SIZE_BYTES != 0) certOut->mPeerPublicKey.exponent.mLength++; READ_REV_BINARY_TO_INT(certOut->mPeerPublicKey.exponent.mData, lenTemp); bufin += sizeof(gsi_u32); //skip the prepended length READ_BINARY(certOut->mServerData, (unsigned int)WS_LOGIN_SERVERDATA_LEN); bufin += sizeof(gsi_u32); //skip the prepended length READ_BINARY(certOut->mSignature, (unsigned int)WS_LOGIN_SERVERDATA_LEN); return gsi_true; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// gsi_bool wsLoginCertWriteXML(const GSLoginCertificate * cert, const char * aNamespace, GSXmlStreamWriter writer) { GS_ASSERT(cert != NULL); GS_ASSERT(writer != NULL); if (gsi_is_false(gsXmlWriteIntElement(writer, aNamespace, "length", cert->mLength)) || gsi_is_false(gsXmlWriteIntElement(writer, aNamespace, "version", cert->mVersion)) || gsi_is_false(gsXmlWriteIntElement(writer, aNamespace, "partnercode", cert->mPartnerCode)) || gsi_is_false(gsXmlWriteIntElement(writer, aNamespace, "namespaceid", cert->mNamespaceId)) || gsi_is_false(gsXmlWriteIntElement(writer, aNamespace, "userid", cert->mUserId)) || gsi_is_false(gsXmlWriteIntElement(writer, aNamespace, "profileid", cert->mProfileId)) || gsi_is_false(gsXmlWriteIntElement(writer, aNamespace, "expiretime", cert->mExpireTime)) || gsi_is_false(gsXmlWriteAsciiStringElement(writer, aNamespace, "profilenick", cert->mProfileNick))|| gsi_is_false(gsXmlWriteAsciiStringElement(writer, aNamespace, "uniquenick", cert->mUniqueNick)) || gsi_is_false(gsXmlWriteAsciiStringElement(writer, aNamespace, "cdkeyhash", cert->mCdKeyHash)) || gsi_is_false(gsXmlWriteLargeIntElement(writer, aNamespace, "peerkeymodulus", &cert->mPeerPublicKey.modulus)) || gsi_is_false(gsXmlWriteLargeIntElement(writer, aNamespace, "peerkeyexponent", &cert->mPeerPublicKey.exponent)) || gsi_is_false(gsXmlWriteHexBinaryElement(writer, aNamespace, "serverdata", cert->mServerData, WS_LOGIN_SERVERDATA_LEN)) || gsi_is_false(gsXmlWriteHexBinaryElement(writer, aNamespace, "signature", cert->mSignature, WS_LOGIN_SIGNATURE_LEN)) ) { //gsLargeIntReverseBytes(&cert->mPeerPublicKey.modulus); //gsLargeIntReverseBytes(&cert->mPeerPublicKey.exponent); return gsi_false; } //gsLargeIntReverseBytes(&cert->mPeerPublicKey.modulus); //gsLargeIntReverseBytes(&cert->mPeerPublicKey.exponent); return gsi_true; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// gsi_bool wsLoginCertReadXML(GSLoginCertificate * cert, GSXmlStreamReader reader) { char hexstr[GS_CRYPT_RSA_BYTE_SIZE*2 +1]; // temp storage for key hex strings int hexlen; GS_ASSERT(cert != NULL); GS_ASSERT(reader != NULL); if (gsi_is_false(gsXmlReadChildAsInt (reader, "length", (int*)&cert->mLength)) || gsi_is_false(gsXmlReadChildAsInt (reader, "version", (int*)&cert->mVersion)) || gsi_is_false(gsXmlReadChildAsInt (reader, "partnercode",(int*)&cert->mPartnerCode)) || gsi_is_false(gsXmlReadChildAsInt (reader, "namespaceid",(int*)&cert->mNamespaceId)) || gsi_is_false(gsXmlReadChildAsInt (reader, "userid", (int*)&cert->mUserId)) || gsi_is_false(gsXmlReadChildAsInt (reader, "profileid", (int*)&cert->mProfileId)) || gsi_is_false(gsXmlReadChildAsInt (reader, "expiretime", (int*)&cert->mExpireTime)) || gsi_is_false(gsXmlReadChildAsTStringNT (reader, "profilenick", cert->mProfileNick, WS_LOGIN_NICK_LEN)) || gsi_is_false(gsXmlReadChildAsTStringNT (reader, "uniquenick", cert->mUniqueNick, WS_LOGIN_UNIQUENICK_LEN)) || gsi_is_false(gsXmlReadChildAsTStringNT (reader, "cdkeyhash", cert->mCdKeyHash, WS_LOGIN_KEYHASH_LEN)) || gsi_is_false(gsXmlReadChildAsStringNT (reader, "peerkeymodulus", hexstr, GS_CRYPT_RSA_BYTE_SIZE*2 +1)) || gsi_is_false(gsLargeIntSetFromHexString(&cert->mPeerPublicKey.modulus, hexstr)) || gsi_is_false(gsXmlReadChildAsStringNT (reader, "peerkeyexponent", hexstr, GS_CRYPT_RSA_BYTE_SIZE*2 +1)) || gsi_is_false(gsLargeIntSetFromHexString(&cert->mPeerPublicKey.exponent, hexstr)) || gsi_is_false(gsXmlReadChildAsHexBinary(reader, "serverdata", cert->mServerData, WS_LOGIN_SERVERDATA_LEN, &hexlen)) || gsi_is_false(gsXmlReadChildAsHexBinary(reader, "signature", cert->mSignature, WS_LOGIN_SIGNATURE_LEN, &hexlen)) ) { return gsi_false; } return gsi_true; } #pragma warning(default: 4267)