gsi_i32 gsCryptRSAPKCS1EncryptBuffer(const gsCryptRSAKey *publicKey, const unsigned char *plainText, gsi_u32 len, unsigned char buffer[GS_CRYPT_RSA_BYTE_SIZE]) { gsi_u8 buf[GS_CRYPT_RSA_BYTE_SIZE]; gsCryptRSAPKCS1Packet* packet = (gsCryptRSAPKCS1Packet*)buf; gsLargeInt_t lintRSAPacket; if (len > (GS_CRYPT_RSA_BYTE_SIZE-11)) // 2 byte header, 8 byte pad minimum, 1 byte separator return -1; // form the packet packet->headerByte[0] = 0x00; packet->headerByte[1] = 0x02; gsiCryptRSAGeneratePad(packet->data, GS_CRYPT_RSA_BYTE_SIZE-len-3); packet->data[GS_CRYPT_RSA_BYTE_SIZE-len-3] = 0x00; // separator memcpy(&packet->data[GS_CRYPT_RSA_BYTE_SIZE-len-3+1], plainText, len); if (gsi_is_false(gsLargeIntSetFromMemoryStream(&lintRSAPacket, (const gsi_u8*)buf, GS_CRYPT_RSA_BYTE_SIZE)) || gsi_is_false(gsLargeIntPowerMod(&lintRSAPacket, &publicKey->exponent, &publicKey->modulus, &lintRSAPacket)) || gsi_is_false(gsLargeIntWriteToMemoryStream(&lintRSAPacket, buffer)) ) { return -1; } return 0; }
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 gsi_i32 gsCryptRSAPKCS1DecryptBuffer(const gsCryptRSAKey *privateKey, const unsigned char cipherText[GS_CRYPT_RSA_BYTE_SIZE], unsigned char *plainText, gsi_u32 *lenout) { int i=0; char* temp; gsLargeInt_t lintRSAPacket; lintRSAPacket.mLength = GS_CRYPT_RSA_BYTE_SIZE/GS_LARGEINT_DIGIT_SIZE_BYTES; memcpy(lintRSAPacket.mData, cipherText, GS_CRYPT_RSA_BYTE_SIZE); if (gsi_is_false(gsLargeIntReverseBytes(&lintRSAPacket)) || // reverse from bytebuffer to lint format gsi_is_false(gsLargeIntPowerMod(&lintRSAPacket, &privateKey->exponent, &privateKey->modulus, &lintRSAPacket)) || gsi_is_false(gsLargeIntReverseBytes(&lintRSAPacket)) // reverse back into a bytebuffer ) { return -1; } // check post exponentiation length if (lintRSAPacket.mLength < (GS_CRYPT_RSA_BYTE_SIZE/GS_LARGEINT_DIGIT_SIZE_BYTES)) return -1; // Check the packet for legality // 1. first byte must be 0x00 // 2. send byte must be 0x02 // 3. pad must be at least 8 bytes and end with 0x00 // 4. payload must be at least 1 byte temp = (char*)lintRSAPacket.mData; if (temp[0] != 0x00) return -1; if (temp[1] != 0x02) return -2; // find the start of the data (first 0x00 byte after the 1st) temp = (char*)lintRSAPacket.mData; for (i=2; i<GS_CRYPT_RSA_BYTE_SIZE; i++) { if (temp[i] == 0) break; } if (i < (2+8)) // 2 byte header, 8 byte minimum pad return -3; // pad too small if (i == GS_CRYPT_RSA_BYTE_SIZE) return -4; // no payload // the rest is the msg memcpy(plainText, (temp+i+1), GS_CRYPT_RSA_BYTE_SIZE); // +1 to skip the 0x00 *lenout = (gsi_u32)(GS_CRYPT_RSA_BYTE_SIZE-(i+1)); // +1 to skip the 0x00 return 0; }
/////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // This runs all the tests static gsi_bool RunTests(gsi_u8 loginMethod, gsi_bool isLoginOnly) { //SCReportPtr aReport = NULL; SCResult aResult = SCResult_NO_ERROR; int i=0, k=0, j=0; // Clear sample data memset(&gServerData, 0, sizeof(gServerData)); memset(gPlayerData, 0, sizeof(gPlayerData)); // Initialize SDK core/common objects for both the auth service and // the Competition SDK gsCoreInitialize(); // Override service URLs for debugging? //strcpy(wsAuthServiceURL, "http://mwstage.gamespy.com/AuthService/AuthService.asmx"); //strcpy(scServiceURL, "http://mwstage.gamespy.com/CompetitionService/CompetitionService.asmx"); //////////////////////////////////////////////// //////////////////////////////////////////////// // Obtain a certificate for each local player. // - For the sample, all player's are local. // Switch up login modes as necessary for testing the authservice if (loginMethod == SCTEST_LOGIN_REMOTEAUTH) { myPlayerLogin(SCTEST_LOGIN_REMOTEAUTH, SCTEST_TOKEN_1, SCTEST_CHALLENGE_1, 0); myPlayerLogin(SCTEST_LOGIN_REMOTEAUTH, SCTEST_TOKEN_2, SCTEST_CHALLENGE_2, 1); } else if (loginMethod == SCTEST_LOGIN_UNIQUE) { myPlayerLogin(SCTEST_LOGIN_UNIQUE, SCTEST_NICK_1, SCTEST_PASSWORD, 0); myPlayerLogin(SCTEST_LOGIN_UNIQUE, SCTEST_NICK_2, SCTEST_PASSWORD, 1); //myPlayerLogin(SCTEST_LOGIN_UNIQUE, SCTEST_NICK_3, SCTEST_PASSWORD, 2); //myPlayerLogin(SCTEST_LOGIN_UNIQUE, SCTEST_NICK_4, SCTEST_PASSWORD, 3); //myPlayerLogin(SCTEST_LOGIN_UNIQUE, SCTEST_NICK_5, SCTEST_PASSWORD, 4); //myPlayerLogin(SCTEST_LOGIN_UNIQUE, SCTEST_NICK_6, SCTEST_PASSWORD, 5); } else if (loginMethod == SCTEST_LOGIN_PROFILE) { myPlayerLogin(SCTEST_LOGIN_PROFILE, SCTEST_NICK_1, SCTEST_PASSWORD, 0); myPlayerLogin(SCTEST_LOGIN_PROFILE, SCTEST_NICK_2, SCTEST_PASSWORD, 1); } if (!isLoginOnly) { // Initialize the SDK for (i=0; i < SCTEST_NUM_PLAYERS; i++) { aResult = scInitialize(SCTEST_GAME_ID, &gPlayerData[i].mStatsInterface); if (aResult != SCResult_NO_ERROR) { gsDebugFormat(GSIDebugCat_App, GSIDebugType_Misc, GSIDebugLevel_HotError, "scInitialize returned %s\r\n", SCResultStr[aResult]); return gsi_false; } } //////////////////////////////////////// //////////////////////////////////////// // Simulate peer-to-peer authentication // You can have the server validate each client. // You may have each client validate every other. // Certificates may also be used to exchange keys for encrypting game traffic. // // Step1 - Trade certificates using unsecured game socket. (The sample doesn't do this) // Step2 - Verify certificate is authentic using wsLoginCertIsValid // Step3 - Verify that the guy who gave you the certificate actually owns it // Step1 - skipped. // - The usual scenario is to have the host verify the client certificate when the player joins. // Step2 - validate certificates for (i=0; i < SCTEST_NUM_PLAYERS; i++) { if (gsi_is_false(wsLoginCertIsValid(&gPlayerData[i].mCertificate))) { gsDebugFormat(GSIDebugCat_App, GSIDebugType_Misc, GSIDebugLevel_HotError, "Error validating certificate for player %d!\r\n", i); return gsi_false; } } // Step3 - exchange keys gsDebugFormat(GSIDebugCat_App, GSIDebugType_Misc, GSIDebugLevel_Debug, "Generating encryption keys\r\n"); for (i=0; i < SCTEST_NUM_PLAYERS; i++) { for (k=i+1; k < SCTEST_NUM_PLAYERS; k++) { SCPeerKeyExchangeMsg exchangeMsg1; SCPeerKeyExchangeMsg exchangeMsg2; const char * plainTextMsg = "Hello Secure!"; char cipherMsg[32] = {'\0'}; int msgLen = (int)strlen(plainTextMsg); gsDebugFormat(GSIDebugCat_App, GSIDebugType_Misc, GSIDebugLevel_Notice, "Creating session keys for player pair %d <-> %d\r\n", i, k); // Each player should create a key for receiving data from the remote player // For extra security, we use a different encryption key for each channel scPeerCipherInit(&gPlayerData[i].mCertificate, &gPlayerData[i].mPeerRecvCipher[k]); // 'i' to receive data from 'k' scPeerCipherInit(&gPlayerData[k].mCertificate, &gPlayerData[k].mPeerRecvCipher[i]); // 'k' to receive data from 'i' // Create a key exchange message for transmitting the key to the other player scPeerCipherCreateKeyExchangeMsg(&gPlayerData[k].mCertificate, &gPlayerData[i].mPeerRecvCipher[k], exchangeMsg1); // from 'i', encrypted with 'k' public key scPeerCipherCreateKeyExchangeMsg(&gPlayerData[i].mCertificate, &gPlayerData[k].mPeerRecvCipher[i], exchangeMsg2); // from 'k', encrypted with 'i' public key // Send it (using game network layer) // - sample doesn't need to send b/c all player's are local // Receiving player should parse the key out of it. // - decrypting the msg requires the local player's private data scPeerCipherParseKeyExchangeMsg(&gPlayerData[k].mCertificate, &gPlayerData[k].mPrivateData, exchangeMsg1, &gPlayerData[k].mPeerSendCipher[i]); // 'k' to send data to 'i' scPeerCipherParseKeyExchangeMsg(&gPlayerData[i].mCertificate, &gPlayerData[i].mPrivateData, exchangeMsg2, &gPlayerData[i].mPeerSendCipher[k]); // 'i' to send data to 'k' // Now we can send secure data by using the (fast) encryption and decryption functions // - Encrypts and Decrypts in place strcpy(cipherMsg, plainTextMsg); scPeerCipherEncryptBufferIV(&gPlayerData[i].mPeerSendCipher[k], 1, (gsi_u8*)cipherMsg, (gsi_u32)msgLen); // 'i' sending to 'k' scPeerCipherDecryptBufferIV(&gPlayerData[k].mPeerRecvCipher[i], 1, (gsi_u8*)cipherMsg, (gsi_u32)msgLen); // 'k' receiving from 'i' } } //////////////////////////////////////// //////////////////////////////////////// // Host creates session // Remember that everyone is on the same machine, so the // first player's login certificate and private data are // used to create a session // REQUIRED FOR MATCHLESS GAMES // For matchless games this function needs to be called so that the Competition // System is aware of the type of reports coming in. scCreateMatchlessSession(gPlayerData[0].mStatsInterface, &gPlayerData[0].mCertificate, &gPlayerData[0].mPrivateData, createSessionCallback, TIMEOUT_MS, NULL); myWaitForCallbacks(gPlayerData[0].mStatsInterface, 1); // End creating the session // The host can now set his connection ID strcpy((char *)gPlayerData[0].mConnectionId, scGetConnectionId(gPlayerData[0].mStatsInterface)); // In most cases the host also has the "authoritative" game view // For this game // Remember that in matchless sessions, the setting for authoritative should // not matter as each player's stats get updated soon after they leave. The // system will not need to wait for all players to submit before processing stats. scSetReportIntention(gPlayerData[0].mStatsInterface, NULL, gsi_false, &gPlayerData[0].mCertificate, &gPlayerData[0].mPrivateData, setReportIntentionCallback, TIMEOUT_MS, &gPlayerData[0]); myWaitForCallbacks(gPlayerData[0].mStatsInterface, 1); for (j=1; j<SCTEST_NUM_PLAYERS; j++) { // The host would then send all other player's the Session ID generated when the session was created // so that they can set their intentions, and retrieve the connection ID for report submissions scSetSessionId(gPlayerData[j].mStatsInterface, (const unsigned char *)scGetSessionId(gPlayerData[0].mStatsInterface)); scSetReportIntention(gPlayerData[j].mStatsInterface, NULL, gsi_false, &gPlayerData[j].mCertificate, &gPlayerData[j].mPrivateData, setReportIntentionCallback, TIMEOUT_MS, &gPlayerData[j]); // store each connection id for the other players //strcpy((char *)gPlayerData[j].mConnectionId, scGetConnectionId(gPlayerData[j].mStatsInterface)); // *NOTE* this is simply meant to show HOW this process works, in truth the connection id // here is overwritten because it's using the same interface so it will report 4 of the same // player information. We need to upgrade the sample to communicate with other instances // in order to report individual player data myWaitForCallbacks(gPlayerData[j].mStatsInterface, 1); } //////////////////////////////////////// //////////////////////////////////////// // Game start // - Record some fake stats // srand(current_time()); // Server settings gServerData.mHostName = _T("scTest"); gServerData.mMapName = _T("scBigMap v2"); gServerData.mGameType = SCTEST_GAMETYPE_CTF; gServerData.mCustomMap = 1; gServerData.mRoundTime = 81.257f; gServerData.mVersion = _T("1.0en"); memset(gServerData.mObfuscationSeed, 0, sizeof(gServerData.mObfuscationSeed)); // Pretend everyone is joining now for (i=0; i < SCTEST_NUM_PLAYERS; i++) { if ((i&1)==0) // even players on hero team, odd players on villans { myRecordPlayerJoined(gPlayerData[i].mProfileId, (gsi_u32)((i&1)==0 ? SCTEST_KEY_TEAM_ARENAID_HEROES : SCTEST_KEY_TEAM_ARENAID_VILLAINS)); } else { myRecordPlayerJoined(gPlayerData[i].mProfileId, (gsi_u32)((i&1)==0 ? SCTEST_KEY_TEAM_ARENAID_HEROES : SCTEST_KEY_TEAM_ARENAID_VILLAINS)); } } // Record some events for (i=0; i < 100; i++) { // Who did the shooting and who got shot? gsi_u32 aShooterId = myGetRandomPlayerId(0); gsi_u32 aShooteeId = myGetRandomPlayerId(aShooterId); // prevent shooter from suiciding // How many bullets were fired? int aRandomNumberOfShots = (int)((float)rand()/RAND_MAX)*20; for (k=0; k < aRandomNumberOfShots; k++) myRecordPlayerFiredGun(aShooterId); // (could do a bulk add also) // 10 shots for the kill! if (aRandomNumberOfShots >= 10) myRecordPlayerFrag(aShooterId, aShooteeId); } // some random point values for (i=0; i < SCTEST_NUM_PLAYERS; i++) myRecordPlayerScored(gPlayerData[i].mProfileId, (gsi_u32)myGetRandomInt(100)); if (!CreateReportAndSubmit()) { gsDebugFormat(GSIDebugCat_App, GSIDebugType_Misc, GSIDebugLevel_Debug, "Failed to Create or Submit one of the Reports\r\n"); } //////////////////////////////////////// //////////////////////////////////////// // Game Over // exit // - or - // clear user data // goto start (for new map, round etc) // Cleanup //scDestroyReport(aReport); //aReport = NULL; for (i=0; i < SCTEST_NUM_PLAYERS; i++) { if (gPlayerData[i].mReport) { scDestroyReport(gPlayerData[i].mReport); gPlayerData[i].mReport= NULL; } if (gPlayerData[i].mStatsInterface) { scShutdown(gPlayerData[i].mStatsInterface); gPlayerData[i].mStatsInterface = NULL; } } } gsCoreShutdown(); // Wait for core shutdown // (should be instantaneous unless you have multiple cores) while(gsCoreIsShutdown() == GSCore_SHUTDOWN_PENDING) { gsCoreThink(0); msleep(5); } GSI_UNUSED(SCResultStr); return gsi_true; }
static SAKERequestResult SAKE_CALL sakeiReadOutputRecords(SAKERequest request, SAKEField ***outputRecordsPtr, int *numRecords, int numFields, char ** fieldNames) { SAKEField **outputRecords; SAKEField *outputRecord; SAKEField *outputField; int recordIndex; int fieldIndex; size_t size; // get to the start of the values if(gsi_is_false(gsXmlMoveToChild(request->mSoapResponse, "values"))) return SAKERequestResult_MALFORMED_RESPONSE; // count the number of records *numRecords = gsXmlCountChildren(request->mSoapResponse, "ArrayOfRecordValue"); // check for no records if(*numRecords == 0) { *outputRecordsPtr = NULL; return SAKERequestResult_SUCCESS; } // allocate the array of records size = (sizeof(SAKEField*) * *numRecords); outputRecords = (SAKEField**)gsimalloc(size); if(!outputRecords) return SAKERequestResult_OUT_OF_MEMORY; memset(outputRecords, 0, size); *outputRecordsPtr = outputRecords; // loop through the records for(recordIndex = 0 ; recordIndex < *numRecords ; recordIndex++) { // advance to this record if(gsi_is_false(gsXmlMoveToNext(request->mSoapResponse, "ArrayOfRecordValue"))) return SAKERequestResult_MALFORMED_RESPONSE; // allocate the array of record fields size = (sizeof(SAKEField) * numFields); outputRecord = (SAKEField*)gsimalloc(size); if(!outputRecord) return SAKERequestResult_OUT_OF_MEMORY; memset(outputRecord, 0, size); outputRecords[recordIndex] = outputRecord; // check for the wrong number of fields in the response if(gsXmlCountChildren(request->mSoapResponse, "RecordValue") != numFields) return SAKERequestResult_MALFORMED_RESPONSE; // fill in the array of fields for this record for(fieldIndex = 0 ; fieldIndex < numFields ; fieldIndex++) { // utility pointer outputField = &outputRecord[fieldIndex]; // set the name for this field outputField->mName = fieldNames[fieldIndex]; // move to this field if(gsi_is_false(gsXmlMoveToNext(request->mSoapResponse, "RecordValue"))) return SAKERequestResult_MALFORMED_RESPONSE; // set the type and value based on the response field if(gsXmlMoveToChild(request->mSoapResponse, "byteValue")) { int value; if(gsi_is_false(gsXmlReadChildAsInt(request->mSoapResponse, "value", &value))) return SAKERequestResult_MALFORMED_RESPONSE; outputField->mType = SAKEFieldType_BYTE; outputField->mValue.mByte = (gsi_u8)value; } else if(gsXmlMoveToChild(request->mSoapResponse, "shortValue")) { int value; if(gsi_is_false(gsXmlReadChildAsInt(request->mSoapResponse, "value", &value))) return SAKERequestResult_MALFORMED_RESPONSE; outputField->mType = SAKEFieldType_SHORT; outputField->mValue.mShort = (gsi_i16)value; } else if(gsXmlMoveToChild(request->mSoapResponse, "intValue")) { int value; if(gsi_is_false(gsXmlReadChildAsInt(request->mSoapResponse, "value", &value))) return SAKERequestResult_MALFORMED_RESPONSE; outputField->mType = SAKEFieldType_INT; outputField->mValue.mInt = (gsi_i32)value; } else if (gsXmlMoveToChild(request->mSoapResponse, "int64Value")) { gsi_i64 value; if (gsi_is_false(gsXmlReadChildAsInt64(request->mSoapResponse, "value", &value))) return SAKERequestResult_MALFORMED_RESPONSE; outputField->mType = SAKEFieldType_INT64; outputField->mValue.mInt64 = value; } else if(gsXmlMoveToChild(request->mSoapResponse, "floatValue")) { float value; if(gsi_is_false(gsXmlReadChildAsFloat(request->mSoapResponse, "value", &value))) return SAKERequestResult_MALFORMED_RESPONSE; outputField->mType = SAKEFieldType_FLOAT; outputField->mValue.mFloat = value; } else if(gsXmlMoveToChild(request->mSoapResponse, "asciiStringValue")) { char *value; int len; if(gsi_is_false(gsXmlReadChildAsString(request->mSoapResponse, "value", (const char**)&value, &len))) return SAKERequestResult_MALFORMED_RESPONSE; if(value) value[len] = '\0'; else value = ""; outputField->mType = SAKEFieldType_ASCII_STRING; outputField->mValue.mAsciiString = value; } else if(gsXmlMoveToChild(request->mSoapResponse, "unicodeStringValue")) { char *value; gsi_u16 *valueUnicode; int len; if(gsi_is_false(gsXmlReadChildAsString(request->mSoapResponse, "value", (const char**)&value, &len))) return SAKERequestResult_MALFORMED_RESPONSE; if(value) value[len] = '\0'; else value = ""; valueUnicode = UTF8ToUCS2StringAlloc(value); if(!valueUnicode) return SAKERequestResult_OUT_OF_MEMORY; outputField->mType = SAKEFieldType_UNICODE_STRING; outputField->mValue.mUnicodeString = valueUnicode; } else if(gsXmlMoveToChild(request->mSoapResponse, "booleanValue")) { char *value; int len; gsi_bool boolval; if(gsi_is_false(gsXmlReadChildAsString(request->mSoapResponse, "value", (const char**)&value, &len))) return SAKERequestResult_MALFORMED_RESPONSE; if(value) { value[len] = '\0'; boolval = (strcmp(value,"true") == 0)?gsi_true:gsi_false; } else boolval = gsi_false; //if returned a NULL value, set bool to false outputField->mType = SAKEFieldType_BOOLEAN; outputField->mValue.mBoolean = boolval; } else if(gsXmlMoveToChild(request->mSoapResponse, "dateAndTimeValue")) { time_t value; if(gsi_is_false(gsXmlReadChildAsDateTimeElement(request->mSoapResponse, "value", &value))) return SAKERequestResult_MALFORMED_RESPONSE; outputField->mType = SAKEFieldType_DATE_AND_TIME; outputField->mValue.mDateAndTime = value; } else if(gsXmlMoveToChild(request->mSoapResponse, "binaryDataValue")) { gsi_u8 *value; int len; if(gsi_is_false(gsXmlReadChildAsBase64Binary(request->mSoapResponse, "value", NULL, &len))) return SAKERequestResult_MALFORMED_RESPONSE; if(len > 0) { value = (gsi_u8*)gsimalloc((size_t)len); if(!value) return SAKERequestResult_OUT_OF_MEMORY; if(gsi_is_false(gsXmlReadChildAsBase64Binary(request->mSoapResponse, "value", value, &len))) { gsifree(value); return SAKERequestResult_MALFORMED_RESPONSE; } } else { value = NULL; len = 0; } outputField->mType = SAKEFieldType_BINARY_DATA; outputField->mValue.mBinaryData.mLength = len; outputField->mValue.mBinaryData.mValue = value; } else { GS_FAIL_STR("No recognized field type found in RecordValue"); return SAKERequestResult_UNKNOWN_ERROR; } } } return SAKERequestResult_SUCCESS; }
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 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 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 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; }
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; }
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; }