Validation
Validation occurs when a game client already expects an authorized session to be active server-side. A session key is echoed back to the server as received during user authentication to continue it's session.
<MSG_USER_VALIDATE>
<RECORD>
<_MsgOrder TYPE="UBYT" NOXFER="TRUE">15</_MsgOrder>
<_MsgName TYPE="STR" NOXFER="TRUE">MSG_USER_VALIDATE</_MsgName>
<_MsgDescription TYPE="STR" NOXFER="TRUE">User Validation Request Message.</_MsgDescription>
<_MsgHandler TYPE="STR" NOXFER="TRUE">MSG_UserValidate</_MsgHandler>
<_MsgAccessLvl TYPE="UBYT" NOXFER="TRUE">1</_MsgAccessLvl>
<UserID TYPE="GID"></UserID>
<PassKey3 TYPE="STR"></PassKey3>
<MachineID TYPE="GID"></MachineID>
<Locale TYPE="STR"></Locale>
<PatchClientID TYPE="STR"></PatchClientID>
</RECORD>
</MSG_USER_VALIDATE>
<MSG_USER_VALIDATE>
<RECORD>
<_MsgOrder TYPE="UBYT" NOXFER="TRUE">15</_MsgOrder>
<_MsgName TYPE="STR" NOXFER="TRUE">MSG_USER_VALIDATE</_MsgName>
<_MsgDescription TYPE="STR" NOXFER="TRUE">User Validation Request Message.</_MsgDescription>
<_MsgHandler TYPE="STR" NOXFER="TRUE">MSG_UserValidate</_MsgHandler>
<_MsgAccessLvl TYPE="UBYT" NOXFER="TRUE">1</_MsgAccessLvl>
<UserID TYPE="GID"></UserID>
<PassKey3 TYPE="STR"></PassKey3>
<MachineID TYPE="GID"></MachineID>
<Locale TYPE="STR"></Locale>
<PatchClientID TYPE="STR"></PatchClientID>
</RECORD>
</MSG_USER_VALIDATE>
Figure A.I -- The MSG_USER_VALIDATE
DML record, as of writing.
PassKey3
PassKey3 is another hashing algorithm used by the game client to securely trasmit said data over the network.
It ia hashed using SHA512 and the initial input is the session key (or ClientKey2) that was given in user authentication.
It is then salted again with a string formatted as $"{sessionID}{timeSecs}{timeMillis}"
, where timeSecs
and timeMillis
are the established times received from the clients's SessionAccept message when the session was created.
public static string EncodePK3(string input, ushort sessionID, uint timeSecs, uint timeMillis)
{
using var sha512 = SHA512.Create();
// Hash the original input, which should be ClientKey2
var bytes = Encoding.UTF8.GetBytes(input);
sha512.TransformBlock(bytes, 0, bytes.Length, null, 0);
// Salt
var sessionInfoBytes = Encoding.UTF8.GetBytes($"{sessionID}{timeSecs}{timeMillis}");
sha512.TransformFinalBlock(sessionInfoBytes, 0, sessionInfoBytes.Length);
var passkey3Bytes = sha512.Hash;
var passkey3 = Convert.ToBase64String(passkey3Bytes);
return passkey3;
}
public static string EncodePK3(string input, ushort sessionID, uint timeSecs, uint timeMillis)
{
using var sha512 = SHA512.Create();
// Hash the original input, which should be ClientKey2
var bytes = Encoding.UTF8.GetBytes(input);
sha512.TransformBlock(bytes, 0, bytes.Length, null, 0);
// Salt
var sessionInfoBytes = Encoding.UTF8.GetBytes($"{sessionID}{timeSecs}{timeMillis}");
sha512.TransformFinalBlock(sessionInfoBytes, 0, sessionInfoBytes.Length);
var passkey3Bytes = sha512.Hash;
var passkey3 = Convert.ToBase64String(passkey3Bytes);
return passkey3;
}
Figure B.I -- PassKey3
method from Imlight's cryptography library.