2023-02-04 21:00:01 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#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";
|
|
|
|
|
2024-09-19 10:52:47 +02:00
|
|
|
// This is declared as an extern so it can be overridden when testing
|
2023-02-04 21:00:01 +01:00
|
|
|
#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)
|