mirror of
https://github.com/TribesNext/t2-scripts.git
synced 2026-01-20 02:14:45 +00:00
204 lines
6.5 KiB
C#
204 lines
6.5 KiB
C#
// Tribes 2 Unofficial Authentication System
|
|
// http://www.tribesnext.com/
|
|
// Written by Electricutioner/Thyth
|
|
// Copyright 2008 by Electricutioner/Thyth and the Tribes 2 Community System Reengineering Intitiative
|
|
|
|
// Version 1.0: 2009-02-13
|
|
|
|
// A little bit of development theory:
|
|
// -The Apotheosis DLL contains 3 RSA public keys. One for authentication, one for updates,
|
|
// and one for delegation. The delegation key forms the root of the community system trust heirarchy.
|
|
// -The delegated-community-enhancement server issues time limited community certificates, which
|
|
// annotate the bare account certificates. The annotations include current name, current clan, current tag
|
|
// and current clan membership so that getAuthInfo() provides all relevant information. These certificates
|
|
// are time limited to enforce the "current" status of the annotations.
|
|
// -Since game servers don't communicate with centralized systems (except for listing), the client is
|
|
// responsible for providing a signed community certificate, and if prompted, the client is also
|
|
// responsible for providing the authoratatively signed certificate from the relevant DCE. Thus, the
|
|
// server will accumilate a small cache of valid DCE certificates.
|
|
|
|
// DCE certificate format:
|
|
// DCEName DCENum IssuedEpoch ExpireEpoch 0 0 e n sig
|
|
// The two zeros are reserved for future use.
|
|
// Community certificate format:
|
|
// DCENum IssuedEpoch ExpireEpoch IssuedForGUID HexBlob Sig
|
|
// HexBlob format:
|
|
// (Follows same format as contents returned by getAuthInfo, but is hex encoded.)
|
|
|
|
// verify with the delegation RSA public key... hard coded in the executable
|
|
function t2csri_verify_deleg_signature(%sig)
|
|
{
|
|
%sig = strReplace(%sig, "\x27", "\\\x27");
|
|
rubyEval("tsEval '$temp=\"' + t2csri_verify_deleg_signature('" @ %sig @ "').to_s(16) + '\";'");
|
|
while (strLen($temp) < 40)
|
|
$temp = "0" @ $temp;
|
|
return $temp;
|
|
}
|
|
|
|
// allow the client to send in an unknown DCE certificate
|
|
function serverCmdt2csri_getDCEChunk(%client, %chunk)
|
|
{
|
|
// client can only send in one DCE
|
|
if (%client.t2csri_sentDCEDone)
|
|
return;
|
|
|
|
%client.t2csri_activeDCE = %client.t2csri_activeDCE @ %chunk;
|
|
if (strlen(%client.t2csri_activeDCE) > 20000)
|
|
{
|
|
%client.setDisconnectReason("DCE certificate is too long.");
|
|
%client.delete();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// client finished sending their DCE. validate it
|
|
function serverCmdt2csri_finishedDCE(%client)
|
|
{
|
|
if (%client.t2csri_sentDCEDone)
|
|
return;
|
|
|
|
%dce = %client.t2csri_activeDCE;
|
|
if (getFieldCount(%dce) != 9)
|
|
{
|
|
%client.setDisconnectReason("DCE certificate format is invalid.");
|
|
%client.delete();
|
|
return;
|
|
}
|
|
%dceName = getField(%dce, 0);
|
|
%dceNum = getField(%dce, 1);
|
|
%dceIssued = getField(%dce, 2);
|
|
%dceExpire = getField(%dce, 3);
|
|
%dceE = getField(%dce, 6);
|
|
%dceN = getField(%dce, 7);
|
|
|
|
// check to see if we already have this certificate
|
|
if ($T2CSRI::DCEE[%dceNum] !$= "")
|
|
{
|
|
// we already have the cert... set the client as done
|
|
%client.t2csri_sentDCEDone = 1;
|
|
%client.t2csri_activeDCE = "";
|
|
return;
|
|
}
|
|
|
|
%dceSig = getField(%dce, 8);
|
|
%sigSha = t2csri_verify_deleg_signature(%dceSig);
|
|
%sumStr = %dceName @ "\t" @ %dceNum @ "\t" @ %dceIssued @ "\t" @ %dceExpire @ "\t";
|
|
%sumStr = %sumStr @ getField(%dce, 4) @ "\t" @ getField(%dce, 5) @ "\t" @ %dceE @ "\t" @ %dceN;
|
|
%calcSha = sha1sum(%sumStr);
|
|
|
|
if (%sigSha !$= %calcSha)
|
|
{
|
|
%client.setDisconnectReason("DCE is not signed by authoritative root.");
|
|
%client.delete();
|
|
return;
|
|
}
|
|
|
|
// passed signature check... now check to see if it has expired/issued time has arrived
|
|
%currentTime = currentEpochTime();
|
|
if (%currentTime < %dceIssued || %currentTime > %dceExpire)
|
|
{
|
|
%client.setDisconnectReason("DCE is not valid for the current time period.");
|
|
%client.delete();
|
|
return;
|
|
}
|
|
|
|
// passed time check... enter it into global data structure
|
|
$T2CSRI::DCEName[%dceNum] = %dceName;
|
|
$T2CSRI::DCEE[%dceNum] = %dceE;
|
|
$T2CSRI::DCEN[%dceNum] = %dceN;
|
|
|
|
// client has successfully sent a DCE
|
|
%client.t2csri_sentDCEDone = 1;
|
|
%client.t2csri_activeDCE = "";
|
|
|
|
// client was pending on a certificate signature check, do that now that we have the DCE cert
|
|
if (%client.t2csri_pendingDCE)
|
|
{
|
|
%client.t2csri_pendingDCE = 0;
|
|
serverCmdt2csri_comCertSendDone(%client);
|
|
}
|
|
}
|
|
|
|
// client sending community cert chunk
|
|
function serverCmdt2csri_sendCommunityCertChunk(%client, %chunk)
|
|
{
|
|
// client can only send in one community cert
|
|
if (%client.t2csri_sentComCertDone)
|
|
return;
|
|
|
|
%client.t2csri_comCert = %client.t2csri_comCert @ %chunk;
|
|
if (strlen(%client.t2csri_comCert) > 20000)
|
|
{
|
|
%client.setDisconnectReason("Community certificate is too long.");
|
|
%client.delete();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// client has sent in a full community certificate... validate and parse it
|
|
function serverCmdt2csri_comCertSendDone(%client)
|
|
{
|
|
if (%client.t2csri_sentComCertDone)
|
|
return;
|
|
|
|
%comCert = %client.t2csri_comCert;
|
|
if (getFieldCount(%comCert) != 6)
|
|
{
|
|
%client.setDisconnectReason("Community certificate format is invalid.");
|
|
%client.delete();
|
|
return;
|
|
}
|
|
|
|
// parse
|
|
%dceNum = getField(%comCert, 0);
|
|
%issued = getField(%comCert, 1);
|
|
%expire = getField(%comCert, 2);
|
|
%guid = getField(%comCert, 3);
|
|
%blob = getField(%comCert, 4);
|
|
%sig = getField(%comCert, 5);
|
|
%sumStr = getFieldS(%comCert, 0, 4);
|
|
%calcSha = sha1Sum(%sumStr);
|
|
|
|
// find the correct DCE
|
|
%e = $T2CSRI::DCEE[%dceNum];
|
|
%n = $T2CSRI::DCEN[%dceNum];
|
|
|
|
// what if we don't have it? ask the client for a copy
|
|
if (%e $= "")
|
|
{
|
|
%client.t2csri_pendingDCE = 1;
|
|
commandToClient(%client, 't2csri_requestUnknownDCECert', %dceNum);
|
|
return;
|
|
}
|
|
|
|
// get the signature SHA1
|
|
rubyEval("tsEval '$temp = \"' + rsa_mod_exp('" @ %sig @ "'.to_i(16), '" @ %e @ "'.to_i(16), '" @ %n @ "'.to_i(16)).to_s(16) + '\";'");
|
|
while (strlen($temp) < 40)
|
|
$temp = "0" @ $temp;
|
|
%sigSha = $temp;
|
|
|
|
if (%sigSha !$= %calcSha)
|
|
{
|
|
%client.setDisconnectReason("Community cert is not signed by a known/valid DCE.");
|
|
%client.delete();
|
|
return;
|
|
}
|
|
|
|
// check expiration
|
|
%currentTime = currentEpochTime();
|
|
if (%currentTime > %expire)
|
|
{
|
|
%client.setDisconnectReason("Community cert has expired. Get a fresh one from the DCE.");
|
|
%client.delete();
|
|
return;
|
|
}
|
|
|
|
// valid cert... set the field for processing in the auth-phase code
|
|
%len = strlen(%blob);
|
|
for (%i = 0; %i < %len; %i += 2)
|
|
{
|
|
%decoded = %decoded @ collapseEscape("\\x" @ getSubStr(%blob, %i, 2));
|
|
}
|
|
%client.t2csri_comInfo = %decoded @ "\n";
|
|
%client.t2csri_sentComCertDone = 1;
|
|
} |