mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-07-16 08:55:52 +00:00
begin live server support
This commit is contained in:
parent
0c9ddb476a
commit
e4ae265184
368 changed files with 17756 additions and 7738 deletions
|
|
@ -1,70 +1,87 @@
|
|||
// 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
|
||||
|
||||
// Authentication Server Connector Version 1.0: 11/06/2008
|
||||
|
||||
function authConnect_findAuthServer()
|
||||
{
|
||||
if ($AuthServer::Address !$= "")
|
||||
return;
|
||||
|
||||
echo("Looking up Authentication Server...");
|
||||
|
||||
if (isObject(AuthConnection))
|
||||
{
|
||||
AuthConnection.disconnect();
|
||||
AuthConnection.delete();
|
||||
}
|
||||
|
||||
new HTTPObject(AuthConnection);
|
||||
AuthConnection.setHeader("Accept", "text/plain");
|
||||
AuthConnection.get("www.tribesnext.com", "auth");
|
||||
}
|
||||
|
||||
function AuthConnection::onLine(%this, %line)
|
||||
{
|
||||
if (getFieldCount(%line) != 2)
|
||||
return;
|
||||
|
||||
%address = getField(%line, 0);
|
||||
%signature = getField(%line, 1);
|
||||
|
||||
%sha1sum = sha1sum(%address);
|
||||
%verifSum = t2csri_verify_auth_signature(%signature);
|
||||
while (strlen(%verifSum) < 40)
|
||||
%verifSum = "0" @ %verifSum;
|
||||
|
||||
if (%sha1sum !$= %verifSum)
|
||||
{
|
||||
error("Authentication server lookup returned an address with an invalid signature.");
|
||||
error("Unable to contact the authentication server.");
|
||||
$AuthServer::Address = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo("Authentication server found at " @ %address @ ". Ready to authenticate.");
|
||||
$AuthServer::Address = %address;
|
||||
}
|
||||
}
|
||||
|
||||
function AuthConnection::onDisconnect(%this)
|
||||
{
|
||||
if ($AuthServer::Address $= "")
|
||||
{
|
||||
error("Authentication server lookup failed.");
|
||||
}
|
||||
|
||||
%this.delete();
|
||||
}
|
||||
|
||||
function AuthConnection::onConnectFailed(%this)
|
||||
{
|
||||
%this.delete();
|
||||
}
|
||||
|
||||
function AuthConnection::onDNSFailed(%this)
|
||||
{
|
||||
%this.delete();
|
||||
}
|
||||
// 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
|
||||
|
||||
// Authentication Server Connector Version 1.0: 11/06/2008
|
||||
|
||||
function authConnect_findAuthServer()
|
||||
{
|
||||
if ($AuthServer::Address !$= "")
|
||||
return;
|
||||
echo("Looking up Authentication Server...");
|
||||
if (isObject(AuthConnection))
|
||||
{
|
||||
AuthConnection.disconnect();
|
||||
AuthConnection.delete();
|
||||
}
|
||||
new TCPObject(AuthConnection);
|
||||
|
||||
%data = "GET /auth HTTP/1.1\r\nHost: www.tribesnext.com\r\nUser-Agent: Tribes 2\r\nConnection: close\r\n\r\n";
|
||||
AuthConnection.data = %data;
|
||||
AuthConnection.connect("www.tribesnext.com:80");
|
||||
$AuthServer::Primed = 0;
|
||||
}
|
||||
|
||||
function AuthConnection::onLine(%this, %line)
|
||||
{
|
||||
if (%line == 411)
|
||||
return;
|
||||
if (trim(%line) $= "")
|
||||
{
|
||||
$AuthServer::Primed = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($AuthServer::Primed)
|
||||
{
|
||||
$AuthServer::Address = %line;
|
||||
%this.disconnect();
|
||||
authConnect_verifyLookup();
|
||||
}
|
||||
}
|
||||
|
||||
function AuthConnection::onConnected(%this)
|
||||
{
|
||||
%this.send(%this.data);
|
||||
}
|
||||
|
||||
function authConnect_verifyLookup()
|
||||
{
|
||||
|
||||
if (getFieldCount($AuthServer::Address) != 2)
|
||||
{
|
||||
$AuthServer::Address = "";
|
||||
error("Authentication server lookup failed.");
|
||||
return;
|
||||
}
|
||||
%address = getField($AuthServer::Address, 0);
|
||||
%signature = getField($AuthServer::Address, 1);
|
||||
|
||||
%sha1sum = sha1sum(%address);
|
||||
%verifSum = t2csri_verify_auth_signature(%signature);
|
||||
while (strlen(%verifSum) < 40)
|
||||
%verifSum = "0" @ %verifSum;
|
||||
if (%sha1sum !$= %verifSum)
|
||||
{
|
||||
// signature verification failed... someone has subverted the auth server lookup
|
||||
error("Authentication server lookup returned an address with an invalid signature.");
|
||||
error("Unable to contact the authentication server.");
|
||||
$AuthServer::Address = "";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo("Authentication server found at " @ %address @ ". Ready to authenticate.");
|
||||
$AuthServer::Address = %address;
|
||||
$AuthServer::Primed = "";
|
||||
}
|
||||
}
|
||||
|
||||
// perform signature verification to prove that the auth server has designated the
|
||||
// provided address
|
||||
function t2csri_verify_auth_signature(%sig)
|
||||
{
|
||||
rubyEval("tsEval '$temp=\"' + t2csri_verify_auth_signature('" @ %sig @ "').to_s(16) + '\";'");
|
||||
return $temp;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ $Authentication::Settings::Timeout = 30000;
|
|||
|
||||
function AuthenticationInterface::onLine(%this, %line)
|
||||
{
|
||||
//warn(%line);
|
||||
if (isEventPending($Authentication::TransactionCompletionSchedule))
|
||||
cancel($Authentication::TransactionCompletionSchedule);
|
||||
|
||||
$Authentication::TransactionCompletionSchedule = schedule(700, 0, Authentication_transactionComplete);
|
||||
|
||||
if ($Authentication::Status::ActiveMode != 0)
|
||||
|
|
@ -102,8 +102,12 @@ function Authentication_transactionComplete()
|
|||
}
|
||||
else if (getWord(%buffer, 0) $= "CERT:")
|
||||
{
|
||||
$Authentication::Status::LastCert = getRecord(%buffer, 0);
|
||||
$Authentication::Status::LastExp = getRecord(%buffer, 1);
|
||||
%cert = getSubStr(%buffer, 0, strstr(%buffer, "\n"));
|
||||
%buffer = getSubStr(%buffer, strstr(%buffer, "\n") + 1, strlen(%buffer));
|
||||
%exp = getSubStr(%buffer, 0, (strstr(%buffer, "\n") == -1 ? strlen(%buffer) : strstr(%buffer, "\n")));
|
||||
|
||||
$Authentication::Status::LastCert = %cert;
|
||||
$Authentication::Status::LastExp = %exp;
|
||||
echo("Authentication: Successfully downloaded certificate and encrypted key.");
|
||||
}
|
||||
else
|
||||
|
|
@ -163,7 +167,6 @@ function Authentication_checkAvail()
|
|||
|
||||
if (isObject(AuthenticationInterface))
|
||||
AuthenticationInterface.delete();
|
||||
|
||||
new TCPObject(AuthenticationInterface);
|
||||
|
||||
AuthenticationInterface.data = "AVAIL\n";
|
||||
|
|
@ -185,7 +188,6 @@ function Authentication_checkName(%name)
|
|||
|
||||
if (isObject(AuthenticationInterface))
|
||||
AuthenticationInterface.delete();
|
||||
|
||||
new TCPObject(AuthenticationInterface);
|
||||
|
||||
AuthenticationInterface.data = "NAME\t" @ %name @ "\n";
|
||||
|
|
@ -207,7 +209,6 @@ function Authentication_recoverAccount(%payload)
|
|||
|
||||
if (isObject(AuthenticationInterface))
|
||||
AuthenticationInterface.delete();
|
||||
|
||||
new TCPObject(AuthenticationInterface);
|
||||
|
||||
AuthenticationInterface.data = "RECOVER\t" @ %payload @ "\n";
|
||||
|
|
@ -229,11 +230,9 @@ function Authentication_registerAccount(%payload)
|
|||
|
||||
if (isObject(AuthenticationInterface))
|
||||
AuthenticationInterface.delete();
|
||||
|
||||
new TCPObject(AuthenticationInterface);
|
||||
|
||||
AuthenticationInterface.data = "SIGN\t" @ %payload @ "\n";
|
||||
|
||||
AuthenticationInterface.connect($AuthServer::Address);
|
||||
$Authentication::TransactionCompletionSchedule = schedule($Authentication::Settings::Timeout, 0, Authentication_transactionComplete);
|
||||
}
|
||||
111
docs/base/@vl2/T2csri.vl2/t2csri/autoupdate.cs
Normal file
111
docs/base/@vl2/T2csri.vl2/t2csri/autoupdate.cs
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
// 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
|
||||
|
||||
// Bare Bones Auto Update System Version 1.0: 11/06/2008
|
||||
|
||||
function authConnect_findAutoUpdater()
|
||||
{
|
||||
if ($AutoUpdater::Address !$= "")
|
||||
return;
|
||||
|
||||
if (isObject(AutoUpdateConnection))
|
||||
{
|
||||
AutoUpdateConnection.disconnect();
|
||||
AutoUpdateConnection.delete();
|
||||
}
|
||||
new TCPObject(AutoUpdateConnection);
|
||||
|
||||
%data = "GET /update HTTP/1.1\r\nHost: www.tribesnext.com\r\nUser-Agent: Tribes 2\r\nConnection: close\r\n\r\n";
|
||||
AutoUpdateConnection.connect("www.tribesnext.com:80");
|
||||
AutoUpdateConnection.schedule(1000, send, %data);
|
||||
}
|
||||
|
||||
function AutoUpdateConnection::onLine(%this, %line)
|
||||
{
|
||||
if (!$AutoUpdater::UpdateFound)
|
||||
{
|
||||
$AutoUpdater::Address = %line;
|
||||
%this.disconnect();
|
||||
autoUpdate_verifyLookup();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isEventPending($AutoUpdate::LastLineSch))
|
||||
cancel($AutoUpdate::LastLineSch);
|
||||
$AutoUpdate::LastLineSch = autoUpdate_applyUpdate();
|
||||
if ($AutoUpdate::UpdateStarted)
|
||||
$AutoUpdate::Buffer = $AutoUpdate::Buffer @ "\n" @ %line;
|
||||
else if (strlen(%line) == 0)
|
||||
$AutoUpdate::UpdateStarted = 1;
|
||||
}
|
||||
}
|
||||
|
||||
function autoUpdate_verifyLookup()
|
||||
{
|
||||
if (getFieldCount($AutoUpdate::Address) != 2)
|
||||
{
|
||||
$AutoUpdater::Address = "";
|
||||
error("No valid update address found.");
|
||||
return;
|
||||
}
|
||||
%address = getField($AutoUpdater::Address, 0);
|
||||
%signature = getField($AutoUpdater::Address, 1);
|
||||
|
||||
%sha1sum = sha1sum(%address);
|
||||
if (%sha1sum !$= t2csri_verify_update_signature(%signature))
|
||||
{
|
||||
// signature verification failed... someone has subverted the auth server lookup
|
||||
error("Auto update lookup returned an address with an invalid signature.");
|
||||
error("Unable to download update without a correct signature.");
|
||||
$AutoUpdater::Address = "";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo("New update found at " @ %address @ ". Ready to download.");
|
||||
$AutoUpdater::Address = %address;
|
||||
$AutoUpdater::UpdateFound = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// perform signature verification to prove that the update server has designated the
|
||||
// provided URL for a download, we don't want people injecting arbitrary code into
|
||||
// user installations
|
||||
function t2csri_verify_update_signature(%sig)
|
||||
{
|
||||
rubyEval("tsEval '$temp=\"' + t2csri_verify_update_signature('" @ %sig @ "') + '\";'");
|
||||
return $temp;
|
||||
}
|
||||
|
||||
function autoUpdate_performUpdate()
|
||||
{
|
||||
if ($AutoUpdater::Address $= "")
|
||||
return;
|
||||
|
||||
if (isObject(AutoUpdateConnection))
|
||||
{
|
||||
AutoUpdateConnection.disconnect();
|
||||
AutoUpdateConnection.delete();
|
||||
}
|
||||
new TCPObject(AutoUpdateConnection);
|
||||
|
||||
%host = getSubStr($AutoUpdater::Address, 0, strstr("/"));
|
||||
%uri = getSubStr($AutoUpdater::Address, strlen(%host), strlen($AutoUpdater::Address));
|
||||
|
||||
%data = "GET " @ %uri @ " HTTP/1.1\nHost: " @ %host @ "\nUser-Agent: Tribes 2\nConnection: close\n\n";
|
||||
AutoUpdateConnection.connect(%host);
|
||||
AutoUpdateConnection.schedule(1000, send, %data);
|
||||
}
|
||||
|
||||
function autoUpdate_applyUpdate()
|
||||
{
|
||||
new FileObject(AutoUpdateFile);
|
||||
AutoUpdateFile.openForWrite("autoUpdate.rb");
|
||||
AutoUpdateFile.writeline($AutoUpdate::Buffer);
|
||||
AutoUpdateFile.close();
|
||||
AutoUpdateFile.delete();
|
||||
|
||||
rubyExec("autoUpdate.rb");
|
||||
}
|
||||
93
docs/base/@vl2/T2csri.vl2/t2csri/bans.cs
Normal file
93
docs/base/@vl2/T2csri.vl2/t2csri/bans.cs
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// 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
|
||||
|
||||
// IP and GUID ban list handling.
|
||||
// These seem to be completely broken in engine, so... here is a script implementation.
|
||||
|
||||
// Still works the same way as before... so scripts will function unmodified.
|
||||
// BanList::add( %guid, %ipAddress, %seconds);
|
||||
// If both GUID and IP address are specified, both types of entries are made on the banlist.
|
||||
|
||||
// gets the current Unix Epoch time from Ruby -- in seconds
|
||||
function currentEpochTime()
|
||||
{
|
||||
rubyEval("tsEval '$temp=' + Time.now.to_i.to_s + ';'");
|
||||
return $temp;
|
||||
}
|
||||
|
||||
// compute the addition in Ruby, due to the Torque script precision problems for >1e6 values
|
||||
function getEpochOffset(%seconds)
|
||||
{
|
||||
rubyEval("tsEval '$temp=' + (Time.now.to_i + " @ %seconds @ ").to_s + ';'");
|
||||
return $temp;
|
||||
}
|
||||
|
||||
// bans are added to the $BanList::GUID and $BanList::IP hash maps as the Unix epoch time
|
||||
// when the ban will expire
|
||||
function BanList::add(%guid, %ipAddress, %seconds)
|
||||
{
|
||||
if (%guid != 0)
|
||||
{
|
||||
// add GUID ban
|
||||
$BanList::GUID[%guid] = getEpochOffset(%seconds);
|
||||
}
|
||||
if (getSubStr(%ipAddress, 0, 3) $= "IP:")
|
||||
{
|
||||
// add IP ban
|
||||
%bareIP = getSubStr(%ipAddress, 3, strLen(%ipAddress));
|
||||
%bareIP = getSubStr(%bareIP, 0, strstr(%bareIP, ":"));
|
||||
%bareIP = strReplace(%bareIP, ".", "_"); // variable access bug workaround
|
||||
|
||||
$BanList::IP[%bareIP] = getEpochOffset(%seconds);
|
||||
}
|
||||
|
||||
// write out the updated bans to the file
|
||||
export("$BanList*", "prefs/banlist.cs");
|
||||
}
|
||||
|
||||
// returns boolean on whether the given client is IP banned or not
|
||||
// true if banned, false if not banned
|
||||
function banList_checkIP(%client)
|
||||
{
|
||||
%ip = %client.getAddress();
|
||||
%ip = getSubStr(%ip, 3, strLen(%ip));
|
||||
%ip = getSubStr(%ip, 0, strstr(%ip, ":"));
|
||||
%ip = strReplace(%ip, ".", "_");
|
||||
|
||||
%time = $BanList::IP[%ip];
|
||||
if (%time !$= "")
|
||||
{
|
||||
//%delta = %time - currentEpochTime();
|
||||
// T2 arithmetic fail again... doing subtraction in Ruby
|
||||
rubyEval("tsEval '$temp=' + (" @ %time @ " - Time.now.to_i).to_s + ';'");
|
||||
%delta = $temp;
|
||||
|
||||
if (%delta > 0)
|
||||
return 1;
|
||||
else
|
||||
deleteVariables("$BanList::IP" @ %ip);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// returns boolean on whether the given GUID is banned or not
|
||||
// true if banned, false if not banned
|
||||
function banList_checkGUID(%guid)
|
||||
{
|
||||
%time = $BanList::GUID[%guid];
|
||||
if (%time !$= "")
|
||||
{
|
||||
//%delta = %time - currentEpochTime();
|
||||
// T2 arithmetic fail again... doing subtraction in Ruby
|
||||
rubyEval("tsEval '$temp=' + (" @ %time @ " - Time.now.to_i).to_s + ';'");
|
||||
%delta = $temp;
|
||||
|
||||
if (%delta > 0)
|
||||
return 1;
|
||||
else
|
||||
deleteVariables("$BanList::GUID" @ %guid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
164
docs/base/@vl2/T2csri.vl2/t2csri/base64.cs
Normal file
164
docs/base/@vl2/T2csri.vl2/t2csri/base64.cs
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
// Torque Script Base64 Utilities
|
||||
// Written by Electricutioner
|
||||
// 10:43 PM 7/13/2005
|
||||
|
||||
// Used under license by the Tribes 2 Community System Re-engineering Intitiative.
|
||||
// License Granted: 10/31/2008
|
||||
|
||||
// necessary for the transfer of arbitrary binary data over ASCII connections
|
||||
function Base64_Encode(%string)
|
||||
{
|
||||
%encoded = "";
|
||||
for (%i = 0; %i < strLen(%string); %i += 3)
|
||||
{
|
||||
%binBlock = "";
|
||||
for (%j = 0; %j < 3; %j++)
|
||||
{
|
||||
%bin = DecToBin(strCmp(getSubStr(%string, %i + %j, 1), ""));
|
||||
while (strLen(%bin) < 8 && strLen(%bin) != 0)
|
||||
%bin = "0" @ %bin;
|
||||
%binBlock = %binBlock @ %bin;
|
||||
}
|
||||
for (%j = 0; %j < 4; %j++)
|
||||
{
|
||||
%bin = getSubStr(%binBlock, 6 * %j, 6);
|
||||
if (%bin !$= "")
|
||||
{
|
||||
while(strLen(%bin) < 6)
|
||||
%bin = %bin @ "0";
|
||||
%encoded = %encoded @ $Base64Utils::Base64Chars[BinToDec(%bin)];
|
||||
}
|
||||
else
|
||||
%encoded = %encoded @ "=";
|
||||
}
|
||||
}
|
||||
return %encoded;
|
||||
}
|
||||
function Base64_Decode(%string)
|
||||
{
|
||||
%decoded = "";
|
||||
for (%i = 0; %i < strLen(%string); %i += 4)
|
||||
{
|
||||
%binBlock = "";
|
||||
for (%j = 0; %j < 4; %j++)
|
||||
{
|
||||
%bin = "";
|
||||
%val = Base64_ValToIndex(strCmp(getSubStr(%string, %i + %j, 1), ""));
|
||||
if (%val != -1)
|
||||
%bin = DecToBin(%val);
|
||||
while (strLen(%bin) < 6 && %val != -1)
|
||||
%bin = "0" @ %bin;
|
||||
%binBlock = %binBlock @ %bin;
|
||||
}
|
||||
for (%j = 0; %j < 3; %j++)
|
||||
{
|
||||
%bin = getSubStr(%binBlock, 8 * %j, 8);
|
||||
while(strLen(%bin) < 8 && strLen(%bin) != 0)
|
||||
%bin = "0" @ %bin;
|
||||
if (%bin !$= "")
|
||||
%decoded = %decoded @ collapseEscape("\\x" @ DecToHex(BinToDec(%bin)));
|
||||
}
|
||||
}
|
||||
|
||||
return %decoded;
|
||||
}
|
||||
// a few conditionals are better than a loop
|
||||
function Base64_ValToIndex(%val)
|
||||
{
|
||||
if (%val > 96 && %val < 123)
|
||||
return %val - 71;
|
||||
else if (%val > 64 && %val < 91)
|
||||
return %val - 65;
|
||||
else if (%val > 47 && %val < 58)
|
||||
return %val + 4;
|
||||
else if (%val == 43)
|
||||
return 62;
|
||||
else if (%val == 47)
|
||||
return 63;
|
||||
else if (%val == 61)
|
||||
return -1;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
//create the character array in a minimum of fuss
|
||||
function Base64_CreateArray()
|
||||
{
|
||||
for (%i = 0; %i < 26; %i++)
|
||||
{
|
||||
$Base64Utils::Base64Chars[%i] = collapseEscape("\\x" @ DecToHex(65 + %i));
|
||||
$Base64Utils::Base64Chars[%i + 26] = collapseEscape("\\x" @ DecToHex(97 + %i));
|
||||
|
||||
if (%i < 10)
|
||||
$Base64Utils::Base64Chars[%i + 52] = %i;
|
||||
}
|
||||
$Base64Utils::Base64Chars[62] = "+";
|
||||
$Base64Utils::Base64Chars[63] = "/";
|
||||
}
|
||||
|
||||
// these binary conversion functions are much better than older ones
|
||||
// these can handle just about any size of input, unlike 8 bit like the previous ones
|
||||
function DecToBin(%dec)
|
||||
{
|
||||
%length = mCeil(mLog(%dec) / mLog(2));
|
||||
%bin = "";
|
||||
for (%i = 0; %i <= %length; %i++)
|
||||
{
|
||||
%test = mPow(2, %length - %i);
|
||||
if (%dec >= %test)
|
||||
{
|
||||
%bin = %bin @ "1";
|
||||
%dec -= %test;
|
||||
}
|
||||
else if (%i > 0)
|
||||
%bin = %bin @ "0";
|
||||
}
|
||||
return %bin;
|
||||
}
|
||||
function BinToDec(%bin)
|
||||
{
|
||||
%dec = 0;
|
||||
for (%i = 0; %i < strLen(%bin); %i++)
|
||||
%dec += getSubStr(%bin, %i, 1) * mPow(2, strLen(%bin) - %i - 1);
|
||||
return %dec;
|
||||
}
|
||||
|
||||
//no length limit
|
||||
function DecToHex(%dec)
|
||||
{
|
||||
%bin = DecToBin(%dec);
|
||||
while (strLen(%bin) % 4 != 0)
|
||||
%bin = "0" @ %bin;
|
||||
|
||||
for (%i = 0; %i < strLen(%bin); %i += 4)
|
||||
{
|
||||
%block = getSubStr(%bin, strLen(%bin) - %i - 4, 4);
|
||||
%part = BinToDec(%block);
|
||||
if (%part > 9)
|
||||
{
|
||||
switch (%part)
|
||||
{
|
||||
case 10:
|
||||
%hex = "a" @ %hex;
|
||||
case 11:
|
||||
%hex = "b" @ %hex;
|
||||
case 12:
|
||||
%hex = "c" @ %hex;
|
||||
case 13:
|
||||
%hex = "d" @ %hex;
|
||||
case 14:
|
||||
%hex = "e" @ %hex;
|
||||
case 15:
|
||||
%hex = "f" @ %hex;
|
||||
}
|
||||
}
|
||||
else
|
||||
%hex = %part @ %hex;
|
||||
}
|
||||
if (strlen(%hex) == 0)
|
||||
return "00";
|
||||
else
|
||||
return %hex;
|
||||
}
|
||||
|
||||
Base64_CreateArray();
|
||||
43
docs/base/@vl2/T2csri.vl2/t2csri/certstore.rb
Normal file
43
docs/base/@vl2/T2csri.vl2/t2csri/certstore.rb
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#
|
||||
# Tribes 2 Community System Reengineering Initiative
|
||||
# Client Side Credential/Certificate Store
|
||||
# Version 1.1 (2009/01/25)
|
||||
#
|
||||
# Written by Electricutioner/Thyth
|
||||
# http://absolous.no-ip.com/
|
||||
# Copyright 2008 - 2009
|
||||
#
|
||||
# Released under the terms of the GNU General Public License v3 or later.
|
||||
# http://www.gnu.org/licenses/gpl.html
|
||||
# Your use of this software is subject to the terms of that license. Use, modification, or distribution
|
||||
# constitutes acceptance of these software terms. This license is the only manner by which you are permitted
|
||||
# to use this software, thus rejection of the license terms prohibits your use of this software.
|
||||
#
|
||||
$accCerts = Hash.new
|
||||
$accPrivateKeys = Hash.new
|
||||
|
||||
def certstore_loadAccounts
|
||||
IO.foreach('public.store') {|line| $accCerts[line.split("\t")[0].downcase] = line.rstrip.lstrip }
|
||||
IO.foreach('private.store') {|line| $accPrivateKeys[line.split("\t")[0].downcase] = line.rstrip.lstrip }
|
||||
end
|
||||
|
||||
def certstore_addAccount(public, private)
|
||||
$accCerts[public.split("\t")[0].downcase] = public
|
||||
$accPrivateKeys[public.split("\t")[0].downcase] = private
|
||||
|
||||
publicstore = File.new('public.store', 'a')
|
||||
publicstore.seek(0, IO::SEEK_END)
|
||||
publicstore.puts(public + "\r\n")
|
||||
publicstore.close
|
||||
|
||||
privatestore = File.new('private.store', 'a')
|
||||
privatestore.seek(0, IO::SEEK_END)
|
||||
privatestore.puts(private + "\r\n")
|
||||
privatestore.close
|
||||
end
|
||||
|
||||
def certstore_listAccounts
|
||||
list = String.new
|
||||
$accCerts.each_key { |username| list = list.rstrip + "\t" + $accCerts[username].split("\t")[0].to_s }
|
||||
return list.lstrip
|
||||
end
|
||||
|
|
@ -8,12 +8,201 @@
|
|||
// load the clan support functions
|
||||
exec("t2csri/clientSideClans.cs");
|
||||
|
||||
// initialize the SHA1 digester in Ruby
|
||||
function t2csri_initDigester()
|
||||
{
|
||||
$SHA1::Initialized = 1;
|
||||
rubyEval("$sha1hasher = SHA1Pure.new");
|
||||
}
|
||||
|
||||
// use Ruby to get the SHA1 hash of the string
|
||||
function sha1sum(%string)
|
||||
{
|
||||
if (!$SHA1::Initialized)
|
||||
t2csri_initDigester();
|
||||
%string = strReplace(%string, "'", "\\'");
|
||||
rubyEval("$sha1hasher.prepare");
|
||||
rubyEval("$sha1hasher.append('" @ %string @ "')");
|
||||
rubyEval("tsEval '$temp=\"' + $sha1hasher.hexdigest + '\";'");
|
||||
%temp = $temp;
|
||||
$temp = "";
|
||||
return %temp;
|
||||
}
|
||||
|
||||
// get the password encrypted private key for the following name
|
||||
// assuming it is installed on the system
|
||||
function t2csri_getEncryptedAccountKey(%name)
|
||||
{
|
||||
return rubyGetValue("$accPrivateKeys['" @ strlwr(%name) @ "']");
|
||||
}
|
||||
|
||||
// get the public certificate key for the following name
|
||||
// assuming it is installed on the system
|
||||
function t2csri_getAccountCertificate(%name)
|
||||
{
|
||||
// check if the name exists
|
||||
%found = 0;
|
||||
for (%i = 0; %i < getFieldCount($accountList); %i++)
|
||||
{
|
||||
if (%name $= getField($accountList, %i))
|
||||
%found = 1;
|
||||
}
|
||||
|
||||
// this is a bit of a hack -- Ruby 1.9.0 has some problems getting the account on the first try
|
||||
%value = "";
|
||||
if (%found)
|
||||
{
|
||||
while (strLen(%value) == 0)
|
||||
{
|
||||
%value = rubyGetValue("$accCerts['" @ strlwr(%name) @ "']");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
%value = rubyGetValue("$accCerts['" @ strlwr(%name) @ "']");
|
||||
}
|
||||
return %value;
|
||||
}
|
||||
|
||||
// prevents a warning generated when leaving a server, and allows the yellow
|
||||
// highlight selection on the warrior screen that indicates the active account
|
||||
function WONGetAuthInfo()
|
||||
{
|
||||
$LoginCertificate = t2csri_getAccountCertificate();
|
||||
return getField($LoginCertificate, 0) @ "\t\t0\t" @ getField($LoginCertificate, 1) @ "\n";
|
||||
return getField($LoginCertificate, 0) @ "\t\t0\t" @ getField($LoginCertificate, 1) @ "\n";
|
||||
}
|
||||
|
||||
// decrypt an RC4 encrypted account key
|
||||
// also used for encryption on the plaintext when generating the account
|
||||
function t2csri_decryptAccountKey(%account, %password, %nonce, %doingEncryption)
|
||||
{
|
||||
%key = sha1sum(%password @ %nonce);
|
||||
|
||||
// initiate RC4 stream state with key
|
||||
%iterations = 256;
|
||||
for (%i = 0; %i < %iterations; %i++)
|
||||
{
|
||||
%SArray[%i] = %i;
|
||||
}
|
||||
%j = 0;
|
||||
for (%i = 0; %i < %iterations; %i++)
|
||||
{
|
||||
%j = (%j + %SArray[%i] + strCmp(getSubStr(%key, %i % strLen(%key), 1), "")) % %iterations;
|
||||
|
||||
//swap(S[i],S[j])
|
||||
%temp = %SArray[%i];
|
||||
%SArray[%i] = %SArray[%j];
|
||||
%SArray[%j] = %temp;
|
||||
}
|
||||
|
||||
// discard 2048 bytes from the start of the stream to avoid the strongly biased first bytes
|
||||
%seedI = 0; %seedJ = 0;
|
||||
for (%i = 0; %i < 2048; %i++)
|
||||
{
|
||||
%seedI = (%seedI + 1) % 256;
|
||||
%seedJ = (%seedJ + %SArray[%seedI]) % 256;
|
||||
|
||||
%temp = %SArray[%seedI];
|
||||
%SArray[%seedI] = %SArray[%seedJ];
|
||||
%SArray[%seedJ] = %temp;
|
||||
}
|
||||
|
||||
// decrypt the account
|
||||
%bytes = strlen(%account) / 2;
|
||||
for (%i = 0; %i < %bytes; %i++)
|
||||
{
|
||||
%seedI = (%seedI + 1) % 256;
|
||||
%seedJ = (%seedJ + %SArray[%seedI]) % 256;
|
||||
|
||||
%temp = %SArray[%seedI];
|
||||
%SArray[%seedI] = %SArray[%seedJ];
|
||||
%SArray[%seedJ] = %temp;
|
||||
|
||||
%schar = %SArray[(%SArray[%seedI] + %SArray[%seedJ]) % 256];
|
||||
%achar = strCmp(collapseEscape("\\x" @ getSubStr(%account, %i * 2, 2)), "");
|
||||
%byte = DecToHex(%schar ^ %achar);
|
||||
if (strLen(%byte) < 2)
|
||||
%byte = "0" @ %byte;
|
||||
%out = %out @ %byte;
|
||||
}
|
||||
|
||||
// verify that the password is correct by checking with the nonce (SHA1 plaintext hash)
|
||||
%hash = sha1sum(%out);
|
||||
if (%hash $= %nonce || %doingEncryption)
|
||||
return %out;
|
||||
else
|
||||
{
|
||||
%out = getSubStr(%out, 0, strlen(%out) - 2);
|
||||
// last 4-bit block was corrupted... try to fix it
|
||||
for (%i = 0; %i < 16; %i++)
|
||||
{
|
||||
%chunk = getSubStr(DecToHex(%i), 1, 1);
|
||||
%hash = sha1sum(%out @ %chunk);
|
||||
if (%hash $= %nonce)
|
||||
return %out @ %chunk;
|
||||
}
|
||||
// last 8-bit block was corrupted... try to fix it
|
||||
for (%i = 0; %i < 256; %i++)
|
||||
{
|
||||
%chunk = DecToHex(%i);
|
||||
%hash = sha1sum(%out @ %chunk);
|
||||
if (%hash $= %nonce)
|
||||
return %out @ %chunk;
|
||||
}
|
||||
|
||||
// looks like the password was still wrong
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function t2csri_encryptAccountKey(%account, %password)
|
||||
{
|
||||
%nonce = sha1sum(%account);
|
||||
return %nonce @ ":" @ t2csri_decryptAccountKey(%account, %password, %nonce, 1);
|
||||
}
|
||||
|
||||
// this does the "login" process internally for accounts that exist
|
||||
// it finds the cert, the private key, decrypts it, and sets up the
|
||||
// RSA key data structures in the Ruby environment.
|
||||
function t2csri_getAccount(%username, %password)
|
||||
{
|
||||
$LoginUsername = %username;
|
||||
$LoginCertificate = t2csri_getAccountCertificate(%username);
|
||||
if ($LoginCertificate $= "")
|
||||
{
|
||||
return "NO_SUCH_ACCOUNT";
|
||||
}
|
||||
|
||||
// split the certificate into its components
|
||||
// username guid e n signature
|
||||
%user = getField($LoginCertificate, 0);
|
||||
%guid = getField($LoginCertificate, 1);
|
||||
%e = getField($LoginCertificate, 2);
|
||||
%n = getField($LoginCertificate, 3);
|
||||
%sig = getField($LoginCertificate, 4);
|
||||
|
||||
// nonce:encrypted
|
||||
%encryptedKey = t2csri_getEncryptedAccountKey(%username);
|
||||
%encryptedKey = getField(%encryptedKey, 1); // strip the username from the field
|
||||
%nonce = getSubStr(%encryptedKey, 0, strstr(%encryptedKey, ":"));
|
||||
%block = getSubStr(%encryptedKey, strLen(%nonce) + 1, strLen(%encryptedKey));
|
||||
%decryptedKey = t2csri_decryptAccountKey(%block, %password, %nonce);
|
||||
if (%decryptedKey $= "")
|
||||
{
|
||||
return "INVALID_PASSWORD";
|
||||
}
|
||||
|
||||
// we have the account, and the properly decrypted private key... interface with Ruby and
|
||||
// insert the data...
|
||||
rubyEval("$accountKey = RSAKey.new");
|
||||
rubyEval("$accountKey.e = '" @ %e @ "'.to_i(16)");
|
||||
rubyEval("$accountKey.n = '" @ %n @ "'.to_i(16)");
|
||||
rubyEval("$accountKey.d = '" @ %decryptedKey @ "'.to_i(16)");
|
||||
// protect the private exponent (d) from reading now.
|
||||
// this will prevent scripts from stealing the private exponent, but still
|
||||
// allows doing decryption using the player's account key
|
||||
rubyEval("$accountKey.protect");
|
||||
|
||||
return "SUCCESS";
|
||||
}
|
||||
|
||||
// this sends a request to the authentication server to retrieve an account that is
|
||||
|
|
@ -34,7 +223,10 @@ function t2csri_downloadAccount(%username, %password)
|
|||
//echo(%authStored);
|
||||
|
||||
// get time in UTC, use it as a nonce to prevent replay attacks
|
||||
%utc = time();
|
||||
rubyEval("tsEval '$temp=\"' + Time.new.getutc.to_s + '\";'");
|
||||
%utc = $temp;
|
||||
$temp = "";
|
||||
//echo(%utc);
|
||||
|
||||
// time/username nonce
|
||||
%timeNonce = sha1sum(%utc @ strlwr(%username));
|
||||
|
|
@ -70,13 +262,14 @@ function t2csri_processDownloadCompletion()
|
|||
%cert = getSubStr(%cert, 6, strlen(%cert));
|
||||
%exp = getField(%cert, 0) @ "\t" @ getSubStr(%exp, 5, strlen(%exp));
|
||||
// add it to the store
|
||||
t2csri_storeAccount(%cert, %exp);
|
||||
rubyEval("certstore_addAccount('" @ %cert @ "','" @ %exp @ "')");
|
||||
|
||||
// refresh the UI
|
||||
$LastLoginKey = $LoginName;
|
||||
LoginEditMenu.clear();
|
||||
LoginEditMenu.populate();
|
||||
LoginEditMenu.setActive(1);
|
||||
LoginEditMenu.setSelected(0);
|
||||
LoginEditBox.clear();
|
||||
}
|
||||
else
|
||||
|
|
@ -93,7 +286,6 @@ function t2csri_processDownloadCompletion()
|
|||
function t2csri_gameServerHexAddress()
|
||||
{
|
||||
%ip = ServerConnection.getAddress();
|
||||
|
||||
%ip = getSubStr(%ip, strstr(%ip, ":") + 1, strlen(%ip));
|
||||
%ip = getSubStr(%ip, 0, strstr(%ip, ":"));
|
||||
%ip = strReplace(%ip, ".", " ");
|
||||
|
|
@ -111,7 +303,7 @@ function t2csri_gameServerHexAddress()
|
|||
// client side interface to communicate with the game server
|
||||
function clientCmdt2csri_pokeClient(%version)
|
||||
{
|
||||
echo("T2CSRI: Authenticating with connected game server. (" @ %version @ ")");
|
||||
echo("T2CSRI: Authenticating with connected game server.");
|
||||
|
||||
// send the community certificate, assuming server is running later than 1.0
|
||||
if (getWord(%version, 1) > 1.0)
|
||||
|
|
@ -119,7 +311,6 @@ function clientCmdt2csri_pokeClient(%version)
|
|||
|
||||
$encryptedchallenge = "";
|
||||
|
||||
$LoginCertificate = t2csri_getAccountCertificate();
|
||||
// send the certificate in 200 byte parts
|
||||
for (%i = 0; %i < strlen($LoginCertificate); %i += 200)
|
||||
{
|
||||
|
|
@ -127,11 +318,10 @@ function clientCmdt2csri_pokeClient(%version)
|
|||
}
|
||||
|
||||
// send a 64 bit challenge to the server to prevent replay attacks
|
||||
$loginchallenge = rand_challenge(18446744073709551615);
|
||||
rubyEval("tsEval '$loginchallenge=\"' + rand(18446744073709551615).to_s(16) + '\";'");
|
||||
// append what the client thinks the server IP address is, for anti-replay purposes
|
||||
$loginchallenge = $loginchallenge @ t2csri_gameServerHexAddress();
|
||||
|
||||
schedule(0, 0, commandToServer, 't2csri_sendChallenge', $loginchallenge);
|
||||
commandToServer('t2csri_sendChallenge', $loginchallenge);
|
||||
|
||||
// at this point, server will validate the signature on the certificate then
|
||||
// proceed to verifying the client has the private part of the key if valid
|
||||
|
|
@ -151,7 +341,8 @@ function clientCmdt2csri_decryptChallenge()
|
|||
%challenge = strlwr($encryptedchallenge);
|
||||
for (%i = 0; %i < strlen(%challenge); %i++)
|
||||
{
|
||||
if (!isxdigit(getSubStr(%challenge, %i, 1)))
|
||||
%char = strcmp(getSubStr(%challenge, %i, 1), "");
|
||||
if ((%char < 48 || %char > 102) || (%char > 57 && %char < 97))
|
||||
{
|
||||
schedule(1000, 0, MessageBoxOK, "REJECTED","Invalid characters in server challenge.");
|
||||
disconnect();
|
||||
|
|
@ -159,11 +350,11 @@ function clientCmdt2csri_decryptChallenge()
|
|||
}
|
||||
}
|
||||
|
||||
%decryptedChallenge = t2csri_rsa_decrypt(%challenge);
|
||||
rubyEval("tsEval '$decryptedChallenge=\"' + $accountKey.decrypt('" @ %challenge @ "'.to_i(16)).to_s(16) + '\";'");
|
||||
|
||||
// verify that the client challenge is intact, and extract the server challenge
|
||||
%replayedClientChallenge = getSubStr(%decryptedChallenge, 0, strLen($loginchallenge));
|
||||
%serverChallenge = getSubStr(%decryptedChallenge, strlen(%replayedClientChallenge), strLen(%decryptedChallenge));
|
||||
%replayedClientChallenge = getSubStr($decryptedChallenge, 0, strLen($loginchallenge));
|
||||
%serverChallenge = getSubStr($decryptedChallenge, strlen(%replayedClientChallenge), strLen($decryptedChallenge));
|
||||
if (%replayedClientChallenge !$= $loginchallenge)
|
||||
{
|
||||
schedule(1000, 0, MessageBoxOK, "REJECTED","Server sent back wrong client challenge.");
|
||||
|
|
@ -186,7 +377,7 @@ function clientCmdt2csri_decryptChallenge()
|
|||
// private exponent -- different x requires different time for x^d, and d bits can be found
|
||||
// if you are really resourceful... adding this schedule kills time accuracy and makes such
|
||||
// a correlation attack very improbable
|
||||
schedule(getRandom(64, 512), 0, commandToServer, 't2csri_challengeResponse', %serverChallenge);
|
||||
schedule(getRandom(128, 512), 0, commandToServer, 't2csri_challengeResponse', %serverChallenge);
|
||||
|
||||
// at this point, server will verify that the challenge is equivalent to the one it sent encrypted
|
||||
// to the client. the only way it can be equivalent is if the client has the private key they
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ function clientCmdt2csri_requestUnknownDCECert(%dceNum)
|
|||
{
|
||||
commandToServer('t2csri_getDCEChunk', getSubStr(%cert, %i, 200));
|
||||
}
|
||||
|
||||
commandToServer('t2csri_finishedDCE');
|
||||
}
|
||||
|
||||
|
|
@ -51,6 +50,5 @@ function t2csri_sendCommunityCert()
|
|||
{
|
||||
commandToServer('t2csri_sendCommunityCertChunk', getSubStr(%cert, %i, 200));
|
||||
}
|
||||
|
||||
commandToServer('t2csri_comCertSendDone');
|
||||
}
|
||||
|
|
|
|||
492
docs/base/@vl2/T2csri.vl2/t2csri/crypto.rb
Normal file
492
docs/base/@vl2/T2csri.vl2/t2csri/crypto.rb
Normal file
|
|
@ -0,0 +1,492 @@
|
|||
#
|
||||
# Tribes 2 Community System Reengineering Initiative
|
||||
# Assymetric Cryptography Identity Provisioning
|
||||
# Version 1.0
|
||||
#
|
||||
# Written by Electricutioner/Thyth
|
||||
# http://absolous.no-ip.com/
|
||||
# Copyright 2008
|
||||
#
|
||||
# Released under the terms of the GNU General Public License v3 or later.
|
||||
# http://www.gnu.org/licenses/gpl.html
|
||||
# Your use of this software is subject to the terms of that license. Use, modification, or distribution
|
||||
# constitutes acceptance of these software terms. This license is the only manner by which you are permitted
|
||||
# to use this software, thus rejection of the license terms prohibits your use of this software.
|
||||
#
|
||||
|
||||
# fast modular exponentiation -- the key to the RSA algorithm
|
||||
# result = (b ^ e) % m
|
||||
def rsa_mod_exp(b, e, m)
|
||||
result = 1
|
||||
while (e > 0)
|
||||
if ((e & 1) == 1)
|
||||
result = (result * b) % m
|
||||
end
|
||||
e = e >> 1
|
||||
b = (b * b) % m
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
# RSA key class to keep things nice and organized
|
||||
class RSAKey
|
||||
# allow reading and writing the key values
|
||||
attr_reader :e, :n, :twister, :strength
|
||||
attr_writer :e, :d, :n, :twister
|
||||
|
||||
# allow protecting the d value so it isn't stolen by evil scripts
|
||||
# once a key is protected, it cannot be deprotected, but it can be used to decrypt
|
||||
def protect
|
||||
@protected = 1
|
||||
end
|
||||
# attribute reader for d that returns nil if key protection is active
|
||||
def d
|
||||
if (@protected == 1)
|
||||
return nil
|
||||
else
|
||||
return @d
|
||||
end
|
||||
end
|
||||
|
||||
# encrypt a message with the public exponent (e)
|
||||
# this could be construed as a misnomer, since this is used to verify authentication
|
||||
# images from the authentication server, and to verify a client has both parts of the key they
|
||||
# claim to have
|
||||
def encrypt(message)
|
||||
rsa_mod_exp(message, @e, @n)
|
||||
end
|
||||
|
||||
# decrypt a message with the private exponent (d), also usable for signing
|
||||
# obviously, this will fail if the instance is only the public part of the key
|
||||
def decrypt(message)
|
||||
rsa_mod_exp(message, @d, @n)
|
||||
end
|
||||
|
||||
# generate a new random RSA key of the specified bitsize
|
||||
# this generates keys that should be resistant to quick factorization techniques
|
||||
def generate(bitsize)
|
||||
p = 0
|
||||
q = 0
|
||||
@n = 100
|
||||
@strength = bitsize
|
||||
|
||||
# test for some conditions that could produce insecure RSA keys
|
||||
# p, q difference to see if Fermat factorization could be successful
|
||||
# p - q must be greater than 2*(n ^ (1/4))
|
||||
while ((p - q).abs < (2 * Math.sqrt(Math.sqrt(@n))))
|
||||
p = createPrime(bitsize / 2, 150)
|
||||
q = createPrime(bitsize / 2, 150)
|
||||
@n = p * q
|
||||
end
|
||||
|
||||
totient = (p - 1) * (q - 1)
|
||||
|
||||
# e must be coprime to the totient. we start at 3 and add 2 whenever coprime test fails
|
||||
@e = 3
|
||||
coprimee = 0
|
||||
while (coprimee)
|
||||
if (@e > 7)
|
||||
# e over 7 has a large chance of not being coprime to the totient
|
||||
generate(bitsize)
|
||||
return
|
||||
end
|
||||
block = extendedEuclid(@e, totient, 0, 1, 1, 0)
|
||||
if (block[0] > 1)
|
||||
@e = @e + 2
|
||||
else
|
||||
coprimee = nil
|
||||
end
|
||||
end
|
||||
|
||||
# calculate the d value such that d * e = 1 mod totient
|
||||
# this calculation is done in the coprime of e verification
|
||||
@d = block[1]
|
||||
while (@d < 0)
|
||||
@d = @d + totient
|
||||
end
|
||||
|
||||
# verify that the generated key is a valid RSA key
|
||||
1.upto(10) do |i|
|
||||
testVal = @twister.randomnumber(bitsize) % @n
|
||||
if (decrypt(encrypt(testVal)) != testVal)
|
||||
# key failed... generate a new one
|
||||
generate(bitsize)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# private methods that people shouldn't be poking without a good reason
|
||||
private
|
||||
# obtain gcd and return the "d" value that we want
|
||||
def extendedEuclid(a, b, c, d, e, f)
|
||||
if (b == 0)
|
||||
block = Array.new(3, 0)
|
||||
block[0] = a; # gcd(a, b)
|
||||
block[1] = e; # coefficient of 'a' and the 'd' value we want
|
||||
block[2] = f; # coefficient of 'b'
|
||||
return block
|
||||
else
|
||||
return extendedEuclid(b, a % b, e - ((a / b) * c), f - ((a / b) * d), c, d);
|
||||
end
|
||||
end
|
||||
|
||||
# create a prime number of the specified bitlength
|
||||
# the number of tests specified will control how many miller-rabin primality tests are run
|
||||
# this function will return a prime number with a high degree of confidence if sufficient
|
||||
# tests are run
|
||||
def createPrime(bitlen, tests)
|
||||
# generate a random number of the specific bitlen
|
||||
p = @twister.randomnumber(bitlen)
|
||||
|
||||
# run the primality tests
|
||||
testrun = 0
|
||||
while (testrun < tests)
|
||||
if (prime?(p))
|
||||
testrun = testrun + 1
|
||||
else # not prime -- generate a new one
|
||||
return createPrime(bitlen, tests)
|
||||
end
|
||||
end
|
||||
return p
|
||||
end
|
||||
|
||||
# run a miller-rabin primality test on the given number
|
||||
# returns true if the number is "probably" prime
|
||||
def prime?(potential)
|
||||
qandm = getqm(potential)
|
||||
if (qandm[0] == -1)
|
||||
return nil
|
||||
end
|
||||
|
||||
bval = @twister.randomnumber(@strength / 2)
|
||||
mval = qandm[1]
|
||||
|
||||
if (rsa_mod_exp(bval, mval, potential) == 1)
|
||||
return 1
|
||||
end
|
||||
j = 0
|
||||
while (j < qandm[0])
|
||||
if ((potential - 1) == rsa_mod_exp(bval, mval, potential))
|
||||
return 1
|
||||
end
|
||||
mval = mval * 2
|
||||
j = j + 1
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
def getqm(p)
|
||||
p = p - 1
|
||||
rt = Array.new(2, 0)
|
||||
if (p & 1 != 0)
|
||||
rt[0] = -1
|
||||
rt[1] = -1
|
||||
return rt
|
||||
end
|
||||
div = p / 2
|
||||
counter = 1
|
||||
while (div & 1 == 0)
|
||||
counter = counter + 1
|
||||
div = div / 2
|
||||
end
|
||||
rt[0] = counter
|
||||
rt[1] = div
|
||||
return rt
|
||||
end
|
||||
end
|
||||
|
||||
# Mersenne Twister pseudo random number generator, modified for cryptographic security
|
||||
# period length should be 20 * (2 ^ 19937 - 1)
|
||||
class MersenneTwister
|
||||
@index = 0
|
||||
|
||||
# build the internal storage array
|
||||
def initialize
|
||||
@mt = Array.new(624, 0)
|
||||
end
|
||||
|
||||
# initialize the generator from a seed, can be done repeatedly
|
||||
def seedgen(seed)
|
||||
@mt[0] = seed
|
||||
1.upto(623) do |i|
|
||||
@mt[i] = 0xffffffff & (1812433243 * (@mt[i - 1] ^ (@mt[i - 1] >> 30)) + i)
|
||||
end
|
||||
generateNumbers
|
||||
end
|
||||
|
||||
# extract a number that does not give away the state of the generator, takes 37 elements from generator
|
||||
# and applies SHA1 on it to get a 20 element number. this is repeated until the required length
|
||||
# is reached, and truncated as necessary to bring it down to the requested bitlen
|
||||
def randomnumber(bits)
|
||||
bytes = bits / 8
|
||||
if (bits % 8 != 0)
|
||||
bytes = bytes + 1
|
||||
end
|
||||
|
||||
produced = 0
|
||||
output = 0
|
||||
stages = 0
|
||||
mask = 0
|
||||
|
||||
sha1hash = SHA1Pure.new
|
||||
while (produced < bytes)
|
||||
sha1hash.prepare
|
||||
1.upto(37) do |i|
|
||||
sha1hash.append(extractNumber().to_s);
|
||||
end
|
||||
digest = sha1hash.hexdigest.to_i(16)
|
||||
output = output | (digest << (160 * stages))
|
||||
produced = produced + 20
|
||||
stages = stages + 1
|
||||
end
|
||||
|
||||
0.upto(bits.to_i) do |i|
|
||||
mask = (mask.to_i << 1) | 1
|
||||
end
|
||||
return (output & mask)
|
||||
end
|
||||
|
||||
private
|
||||
# extract a tempered pseudorandom number
|
||||
def extractNumber()
|
||||
if (@index == 0)
|
||||
generateNumbers()
|
||||
end
|
||||
|
||||
y = @mt[@index.to_i]
|
||||
y = y ^ (y >> 11)
|
||||
y = y ^ ((y << 7) & 2636928640)
|
||||
y = y ^ ((y << 15) & 4022730752)
|
||||
y = y ^ (y >> 18)
|
||||
y = y & 0xffffffff
|
||||
|
||||
@index = (@index.to_i + 1) % 624
|
||||
return y
|
||||
end
|
||||
|
||||
# generate 624 untempered numbers for this generator's array
|
||||
def generateNumbers()
|
||||
0.upto(623) do |i|
|
||||
y = (@mt[i] & 0x80000000) + (@mt[(i + 1) % 624] & 0x7FFFFFFF)
|
||||
@mt[i] = @mt[(i + 397) % 624] ^ (y >> 1)
|
||||
if (y & 1 == 1)
|
||||
@mt[i] = @mt[i] ^ 2567483615
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# SHA1 in Pure Ruby
|
||||
class SHA1Pure
|
||||
|
||||
def initialize
|
||||
prepare
|
||||
end
|
||||
|
||||
# prepare the hash digester for a new hash
|
||||
def prepare
|
||||
@state = Array.new(5, 0)
|
||||
@block = Array.new(16, 0)
|
||||
@blockIndex = 0
|
||||
@count = 0
|
||||
|
||||
@state[0] = 0x67452301
|
||||
@state[1] = 0xefcdab89
|
||||
@state[2] = 0x98badcfe
|
||||
@state[3] = 0x10325476
|
||||
@state[4] = 0xc3d2e1f0
|
||||
end
|
||||
|
||||
# append a string to the string being digested
|
||||
def append(str)
|
||||
str = str.to_s
|
||||
str.each_byte {|c| update(c.to_i & 0xff)}
|
||||
end
|
||||
|
||||
# produce a hexidecimal digest string
|
||||
def hexdigest
|
||||
bits = Array.new(8, 0)
|
||||
0.upto(7) do |i|
|
||||
bits[i] = (@count >> (((7 - i) * 8) & 0xff)) & 0xff
|
||||
end
|
||||
update(128)
|
||||
while (@blockIndex != 56)
|
||||
update(0)
|
||||
end
|
||||
0.upto(7) do |i|
|
||||
update(bits[i])
|
||||
end # this will accomplish a transform
|
||||
|
||||
# output the digest
|
||||
digest = ""
|
||||
0.upto(4) do |i|
|
||||
chunk = @state[i].to_s(16)
|
||||
while(chunk.length < 8)
|
||||
chunk = "0" + chunk
|
||||
end
|
||||
digest = digest + chunk
|
||||
end
|
||||
prepare
|
||||
return digest
|
||||
end
|
||||
|
||||
private
|
||||
def rol(val, bits)
|
||||
val = val.to_i
|
||||
bits = bits.to_i
|
||||
return (val << bits) | (val >> (32 - bits))
|
||||
end
|
||||
|
||||
def blk0(i)
|
||||
i = i.to_i
|
||||
@block[i] = (rol(@block[i], 24) & 0xff00ff00) | (rol(@block[i], 8) & 0xff00ff)
|
||||
@block[i] = @block[i] & 0xffffffff
|
||||
return @block[i]
|
||||
end
|
||||
|
||||
def blk(i)
|
||||
i = i.to_i
|
||||
@block[i & 15] = rol(@block[(i + 13) & 15] ^ @block[(i + 8) & 15] ^ @block[(i + 2) & 15] ^ @block[i & 15], 1)
|
||||
@block[i & 15] = @block[i & 15] & 0xffffffff
|
||||
return @block[i & 15]
|
||||
end
|
||||
|
||||
def r0(data, v, w, x, y, z, i)
|
||||
data[z] += ((data[w] & (data[x] ^ data[y])) ^ data[y]) + blk0(i) + 0x5a827999 + rol(data[v], 5)
|
||||
data[z] = data[z] & 0xffffffff
|
||||
data[w] = rol(data[w], 30) & 0xffffffff
|
||||
end
|
||||
|
||||
def r1(data, v, w, x, y, z, i)
|
||||
data[z] += ((data[w] & (data[x] ^ data[y])) ^ data[y]) + blk(i) + 0x5a827999 + rol(data[v], 5)
|
||||
data[z] = data[z] & 0xffffffff
|
||||
data[w] = rol(data[w], 30) & 0xffffffff
|
||||
end
|
||||
|
||||
def r2(data, v, w, x, y, z, i)
|
||||
data[z] += (data[w] ^ data[x] ^ data[y]) + blk(i) + 0x6ed9eba1 + rol(data[v], 5)
|
||||
data[z] = data[z] & 0xffffffff
|
||||
data[w] = rol(data[w], 30) & 0xffffffff
|
||||
end
|
||||
|
||||
def r3(data, v, w, x, y, z, i)
|
||||
data[z] += (((data[w] | data[x]) & data[y]) | (data[w] & data[x])) + blk(i) + 0x8f1bbcdc + rol(data[v], 5)
|
||||
data[z] = data[z] & 0xffffffff
|
||||
data[w] = rol(data[w], 30) & 0xffffffff
|
||||
end
|
||||
|
||||
def r4(data, v, w, x, y, z, i)
|
||||
data[z] += (data[w] ^ data[x] ^ data[y]) + blk(i) + 0xca62c1d6 + rol(data[v], 5)
|
||||
data[z] = data[z] & 0xffffffff
|
||||
data[w] = rol(data[w], 30) & 0xffffffff
|
||||
end
|
||||
|
||||
def transform
|
||||
dd = Array.new(5, 0)
|
||||
dd[0] = @state[0]
|
||||
dd[1] = @state[1]
|
||||
dd[2] = @state[2]
|
||||
dd[3] = @state[3]
|
||||
dd[4] = @state[4]
|
||||
|
||||
r0(dd,0,1,2,3,4, 0)
|
||||
r0(dd,4,0,1,2,3, 1)
|
||||
r0(dd,3,4,0,1,2, 2)
|
||||
r0(dd,2,3,4,0,1, 3)
|
||||
r0(dd,1,2,3,4,0, 4)
|
||||
r0(dd,0,1,2,3,4, 5)
|
||||
r0(dd,4,0,1,2,3, 6)
|
||||
r0(dd,3,4,0,1,2, 7)
|
||||
r0(dd,2,3,4,0,1, 8)
|
||||
r0(dd,1,2,3,4,0, 9)
|
||||
r0(dd,0,1,2,3,4,10)
|
||||
r0(dd,4,0,1,2,3,11)
|
||||
r0(dd,3,4,0,1,2,12)
|
||||
r0(dd,2,3,4,0,1,13)
|
||||
r0(dd,1,2,3,4,0,14)
|
||||
r0(dd,0,1,2,3,4,15)
|
||||
r1(dd,4,0,1,2,3,16)
|
||||
r1(dd,3,4,0,1,2,17)
|
||||
r1(dd,2,3,4,0,1,18)
|
||||
r1(dd,1,2,3,4,0,19)
|
||||
r2(dd,0,1,2,3,4,20)
|
||||
r2(dd,4,0,1,2,3,21)
|
||||
r2(dd,3,4,0,1,2,22)
|
||||
r2(dd,2,3,4,0,1,23)
|
||||
r2(dd,1,2,3,4,0,24)
|
||||
r2(dd,0,1,2,3,4,25)
|
||||
r2(dd,4,0,1,2,3,26)
|
||||
r2(dd,3,4,0,1,2,27)
|
||||
r2(dd,2,3,4,0,1,28)
|
||||
r2(dd,1,2,3,4,0,29)
|
||||
r2(dd,0,1,2,3,4,30)
|
||||
r2(dd,4,0,1,2,3,31)
|
||||
r2(dd,3,4,0,1,2,32)
|
||||
r2(dd,2,3,4,0,1,33)
|
||||
r2(dd,1,2,3,4,0,34)
|
||||
r2(dd,0,1,2,3,4,35)
|
||||
r2(dd,4,0,1,2,3,36)
|
||||
r2(dd,3,4,0,1,2,37)
|
||||
r2(dd,2,3,4,0,1,38)
|
||||
r2(dd,1,2,3,4,0,39)
|
||||
r3(dd,0,1,2,3,4,40)
|
||||
r3(dd,4,0,1,2,3,41)
|
||||
r3(dd,3,4,0,1,2,42)
|
||||
r3(dd,2,3,4,0,1,43)
|
||||
r3(dd,1,2,3,4,0,44)
|
||||
r3(dd,0,1,2,3,4,45)
|
||||
r3(dd,4,0,1,2,3,46)
|
||||
r3(dd,3,4,0,1,2,47)
|
||||
r3(dd,2,3,4,0,1,48)
|
||||
r3(dd,1,2,3,4,0,49)
|
||||
r3(dd,0,1,2,3,4,50)
|
||||
r3(dd,4,0,1,2,3,51)
|
||||
r3(dd,3,4,0,1,2,52)
|
||||
r3(dd,2,3,4,0,1,53)
|
||||
r3(dd,1,2,3,4,0,54)
|
||||
r3(dd,0,1,2,3,4,55)
|
||||
r3(dd,4,0,1,2,3,56)
|
||||
r3(dd,3,4,0,1,2,57)
|
||||
r3(dd,2,3,4,0,1,58)
|
||||
r3(dd,1,2,3,4,0,59)
|
||||
r4(dd,0,1,2,3,4,60)
|
||||
r4(dd,4,0,1,2,3,61)
|
||||
r4(dd,3,4,0,1,2,62)
|
||||
r4(dd,2,3,4,0,1,63)
|
||||
r4(dd,1,2,3,4,0,64)
|
||||
r4(dd,0,1,2,3,4,65)
|
||||
r4(dd,4,0,1,2,3,66)
|
||||
r4(dd,3,4,0,1,2,67)
|
||||
r4(dd,2,3,4,0,1,68)
|
||||
r4(dd,1,2,3,4,0,69)
|
||||
r4(dd,0,1,2,3,4,70)
|
||||
r4(dd,4,0,1,2,3,71)
|
||||
r4(dd,3,4,0,1,2,72)
|
||||
r4(dd,2,3,4,0,1,73)
|
||||
r4(dd,1,2,3,4,0,74)
|
||||
r4(dd,0,1,2,3,4,75)
|
||||
r4(dd,4,0,1,2,3,76)
|
||||
r4(dd,3,4,0,1,2,77)
|
||||
r4(dd,2,3,4,0,1,78)
|
||||
r4(dd,1,2,3,4,0,79)
|
||||
|
||||
@state[0] = (@state[0] + dd[0]) & 0xffffffff
|
||||
@state[1] = (@state[1] + dd[1]) & 0xffffffff
|
||||
@state[2] = (@state[2] + dd[2]) & 0xffffffff
|
||||
@state[3] = (@state[3] + dd[3]) & 0xffffffff
|
||||
@state[4] = (@state[4] + dd[4]) & 0xffffffff
|
||||
end
|
||||
|
||||
def update(b)
|
||||
mask = (8 * (@blockIndex & 3))
|
||||
@count = @count + 8
|
||||
@block[@blockIndex >> 2] = @block[@blockIndex >> 2] & ~(0xff << mask)
|
||||
@block[@blockIndex >> 2] = @block[@blockIndex >> 2] | ((b & 0xff) << mask)
|
||||
@blockIndex = @blockIndex + 1
|
||||
if (@blockIndex == 64)
|
||||
transform
|
||||
@blockIndex = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -11,8 +11,17 @@
|
|||
// load the torque script components
|
||||
exec("t2csri/authconnect.cs");
|
||||
exec("t2csri/authinterface.cs");
|
||||
exec("t2csri/base64.cs");
|
||||
exec("t2csri/clientSide.cs");
|
||||
exec("t2csri/ipv4.cs");
|
||||
exec("t2csri/rubyUtils.cs");
|
||||
|
||||
// load the Ruby components
|
||||
rubyExec("t2csri/crypto.rb");
|
||||
rubyExec("t2csri/certstore.rb");
|
||||
|
||||
rubyEval("certstore_loadAccounts");
|
||||
rubyEval("tsEval '$RubyEnabled=1;'");
|
||||
|
||||
// connect to the auth server via signed lookup
|
||||
schedule(32, 0, authConnect_findAuthServer);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// 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
|
||||
|
||||
// IPv4 Utils Version 1.1 (03/26/2008)
|
||||
|
|
@ -10,7 +11,7 @@
|
|||
// when the game launches, so there shouldn't be more than a
|
||||
// couple of hundred hits per day from the entire T2 community.
|
||||
|
||||
$IPv4::AutomationURL = "/whatismyip";
|
||||
$IPv4::AutomationURL = "/whatismyip.php";
|
||||
|
||||
function ipv4_getInetAddress()
|
||||
{
|
||||
|
|
@ -22,20 +23,20 @@ function ipv4_getInetAddress()
|
|||
IPv4Connection.disconnect();
|
||||
IPv4Connection.delete();
|
||||
}
|
||||
new TCPObject(IPv4Connection);
|
||||
IPV4Connection.data = "GET " @ $IPv4::AutomationURL @ " HTTP/1.1\r\nHost: www.tribesnext.com\r\nUser-Agent: Tribes 2\r\nConnection: close\r\n\r\n";
|
||||
IPv4Connection.connect("www.tribesnext.com:80");
|
||||
}
|
||||
|
||||
new HTTPObject(IPv4Connection)
|
||||
{
|
||||
enableIPv6 = false;
|
||||
};
|
||||
|
||||
IPv4Connection.get("master.tribesnext.com", $IPv4::AutomationURL);
|
||||
function IPv4Connection::onConnected(%this)
|
||||
{
|
||||
%this.send(%this.data);
|
||||
}
|
||||
|
||||
function IPv4Connection::onLine(%this, %line)
|
||||
{
|
||||
if (%line $= "" || %line == 0)
|
||||
return;
|
||||
|
||||
$IPv4::InetAddress = %line;
|
||||
%this.disconnect();
|
||||
}
|
||||
|
|
@ -93,12 +94,13 @@ function ipv4_reasonableConnection(%source, %destination)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// convert a (big endian) hex block into a numeric IP
|
||||
function ipv4_hexBlockToIP(%hex)
|
||||
{
|
||||
for (%i = 0; %i < 4; %i++)
|
||||
{
|
||||
%ip = %ip @ "." @ ord(collapseEscape("\\x" @ getSubStr(%hex, %i * 2, 2)));
|
||||
%ip = %ip @ "." @ strcmp(collapseEscape("\\x" @ getSubStr(%hex, %i * 2, 2)), "");
|
||||
}
|
||||
return getSubStr(%ip, 1, strlen(%ip) - 1);
|
||||
}
|
||||
|
|
|
|||
31
docs/base/@vl2/T2csri.vl2/t2csri/rubyUtils.cs
Normal file
31
docs/base/@vl2/T2csri.vl2/t2csri/rubyUtils.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Tribes 2 Unofficial Authentication System
|
||||
// http://www.tribesnext.com/
|
||||
// Written by Electricutioner/Thyth
|
||||
// Copyright 2008-2009 by Electricutioner/Thyth and the Tribes 2 Community System Reengineering Intitiative
|
||||
|
||||
// Ruby Interface Utilities Version 1.3 (01/27/2009)
|
||||
|
||||
// loads a ruby script
|
||||
function rubyExec(%script)
|
||||
{
|
||||
echo("Loading Ruby script " @ %script @ ".");
|
||||
new FileObject("RubyExecutor");
|
||||
RubyExecutor.openForRead(%script);
|
||||
|
||||
while (!RubyExecutor.isEOF())
|
||||
{
|
||||
%line = RubyExecutor.readLine();
|
||||
%buffer = %buffer @ "\n" @ %line;
|
||||
}
|
||||
rubyEval(%buffer);
|
||||
RubyExecutor.close();
|
||||
RubyExecutor.delete();
|
||||
}
|
||||
|
||||
// extracts a value from the Ruby interpreter environment
|
||||
function rubyGetValue(%value)
|
||||
{
|
||||
$temp = "";
|
||||
rubyEval("tsEval '$temp=\"' + " @ %value @ " + '\";'");
|
||||
return $temp;
|
||||
}
|
||||
|
|
@ -1,10 +1,41 @@
|
|||
// 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.3: 2009-04-23
|
||||
// Version 1.2: 2009-02-16
|
||||
// Clan/Rename Certificate support is included in this version.
|
||||
|
||||
// initialize the SHA1 digester in Ruby
|
||||
function t2csri_initDigester()
|
||||
{
|
||||
$SHA1::Initialized = 1;
|
||||
rubyEval("$sha1hasher = SHA1Pure.new");
|
||||
}
|
||||
|
||||
// use Ruby to get the SHA1 hash of the string
|
||||
function sha1sum(%string)
|
||||
{
|
||||
if (!$SHA1::Initialized)
|
||||
t2csri_initDigester();
|
||||
%string = strReplace(%string, "'", "\\'");
|
||||
rubyEval("$sha1hasher.prepare");
|
||||
rubyEval("$sha1hasher.append('" @ %string @ "')");
|
||||
rubyEval("tsEval '$temp=\"' + $sha1hasher.hexdigest + '\";'");
|
||||
%temp = $temp;
|
||||
$temp = "";
|
||||
return %temp;
|
||||
}
|
||||
|
||||
// verify with the auth server's RSA public key... hard coded in the executable
|
||||
function t2csri_verify_auth_signature(%sig)
|
||||
{
|
||||
rubyEval("tsEval '$temp=\"' + t2csri_verify_auth_signature('" @ %sig @ "').to_s(16) + '\";'");
|
||||
while (strLen($temp) < 40)
|
||||
$temp = "0" @ $temp;
|
||||
return $temp;
|
||||
}
|
||||
|
||||
// server sends the client a certificate in chunks, since they can be rather large
|
||||
function serverCmdt2csri_sendCertChunk(%client, %chunk)
|
||||
{
|
||||
|
|
@ -16,7 +47,7 @@ function serverCmdt2csri_sendCertChunk(%client, %chunk)
|
|||
if (strlen(%client.t2csri_cert) > 20000)
|
||||
{
|
||||
%client.setDisconnectReason("Account certificate too long. Check your account key for corruption.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -46,43 +77,20 @@ function serverCmdt2csri_sendChallenge(%client, %clientChallenge)
|
|||
if (%client.doneAuthenticating)
|
||||
return;
|
||||
|
||||
if (%client.t2csri_retryChallenge)
|
||||
{
|
||||
if (isEventPending(%client.t2csri_retryChallenge))
|
||||
cancel(%client.t2csri_retryChallenge);
|
||||
|
||||
%client.t2csri_retryChallenge = "";
|
||||
}
|
||||
|
||||
if (!%client.t2csri_sentComCertDone && strLen(%client.t2csri_comCert) > 0)
|
||||
{
|
||||
%client.t2csri_retryChallenge = schedule(250, 0, serverCmdt2csri_sendChallenge, %client, %clientChallenge);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen(%client.t2csri_cert) < 1024)
|
||||
{
|
||||
%client.setDisconnectReason("Invalid authentication certificate.");
|
||||
%client.schedule(0, delete);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// echo("Client requesting challenge. CC: " @ %clientChallenge);
|
||||
// echo("Client's certificate: " @ %client.t2csri_cert);
|
||||
//echo("Client requesting challenge. CC: " @ %clientChallenge);
|
||||
//echo("Client's certificate: " @ %client.t2csri_cert);
|
||||
// verify that the certificate the client sent is signed by the authentication server
|
||||
%user = strReplace(getField(%client.t2csri_cert, 0), "\x27", "\\\x27");
|
||||
|
||||
%guid = getField(%client.t2csri_cert, 1);
|
||||
|
||||
// sanitize GUID
|
||||
for (%i = 0; %i < strlen(%guid); %i++)
|
||||
{
|
||||
%char = ord(getSubStr(%guid, %i, 1));
|
||||
%char = strcmp(getSubStr(%guid, %i, 1), "");
|
||||
if (%char > 57 || %char < 48)
|
||||
{
|
||||
%client.setDisconnectReason("Invalid characters in client GUID.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -95,10 +103,11 @@ function serverCmdt2csri_sendChallenge(%client, %clientChallenge)
|
|||
%rsa_chunk = strlwr(%e @ %n @ %sig);
|
||||
for (%i = 0; %i < strlen(%rsa_chunk); %i++)
|
||||
{
|
||||
if (!isxdigit(getSubStr(%rsa_chunk, %i, 1)))
|
||||
%char = strcmp(getSubStr(%rsa_chunk, %i, 1), "");
|
||||
if ((%char < 48 || %char > 102) || (%char > 57 && %char < 97))
|
||||
{
|
||||
%client.setDisconnectReason("Invalid characters in certificate RSA fields.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -106,16 +115,11 @@ function serverCmdt2csri_sendChallenge(%client, %clientChallenge)
|
|||
// get a SHA1 sum
|
||||
%sumStr = %user @ "\t" @ %guid @ "\t" @ %e @ "\t" @ %n;
|
||||
%certSum = sha1sum(%sumStr);
|
||||
|
||||
while (strLen(%sig) < 1024)
|
||||
%sig = "0" @ %sig;
|
||||
|
||||
%verifSum = t2csri_verify_auth_signature(%sig);
|
||||
while (strLen(%verifSum) < 40)
|
||||
%verifSum = "0" @ %verifSum;
|
||||
|
||||
// echo("Calc'd SHA1: " @ %certSum);
|
||||
// echo("Signed SHA1: " @ %verifSum);
|
||||
//echo("Calc'd SHA1: " @ %certSum);
|
||||
//echo("Signed SHA1: " @ %verifSum);
|
||||
|
||||
// verify signature
|
||||
if (%verifSum !$= %certSum)
|
||||
|
|
@ -123,7 +127,7 @@ function serverCmdt2csri_sendChallenge(%client, %clientChallenge)
|
|||
// client supplied a bogus certificate that was never signed by the auth server
|
||||
// abort their connection
|
||||
%client.setDisconnectReason("Invalid account certificate.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -135,10 +139,11 @@ function serverCmdt2csri_sendChallenge(%client, %clientChallenge)
|
|||
%clientChallenge = strlwr(%clientChallenge);
|
||||
for (%i = 0; %i < strlen(%clientChallenge); %i++)
|
||||
{
|
||||
if (!isxdigit(getSubStr(%clientChallenge, %i, 1)))
|
||||
%char = strcmp(getSubStr(%clientChallenge, %i, 1), "");
|
||||
if ((%char < 48 || %char > 102) || (%char > 57 && %char < 97))
|
||||
{
|
||||
%client.setDisconnectReason("Invalid characters in client challenge.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -150,25 +155,22 @@ function serverCmdt2csri_sendChallenge(%client, %clientChallenge)
|
|||
if (!ipv4_reasonableConnection(%sourceIP, %sanityIP))
|
||||
{
|
||||
%client.setDisconnectReason("Potential man in the middle attack detected. Your client claims it connected to: " @ %sanityIP @ ", but the server does not consider this reasonable.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate a random 64-bit server side challenge
|
||||
%client.t2csri_serverChallenge = rand_challenge() @ t2csri_gameClientHexAddress(%client);
|
||||
rubyEval("tsEval '$temp=\"' + rand(18446744073709551615).to_s(16) + '\";'");
|
||||
%client.t2csri_serverChallenge = $temp @ t2csri_gameClientHexAddress(%client);
|
||||
|
||||
%fullChallenge = %client.t2csri_clientChallenge @ %client.t2csri_serverChallenge;
|
||||
if (strlen(%fullChallenge) % 2)
|
||||
%fullChallenge = "0" @ %fullChallenge;
|
||||
|
||||
%temp = rsa_mod_exp(%fullChallenge, %e, %n);
|
||||
rubyEval("tsEval '$temp=\"' + rsa_mod_exp('" @ %fullChallenge @ "'.to_i(16), '" @ %e @ "'.to_i(16), '" @ %n @ "'.to_i(16)).to_s(16) + '\";'");
|
||||
|
||||
// send the challenge in 200 byte chunks
|
||||
for (%i = 0; %i < strlen(%temp); %i += 200)
|
||||
for (%i = 0; %i < strlen($temp); %i += 200)
|
||||
{
|
||||
commandToClient(%client, 't2csri_getChallengeChunk', getSubStr(%temp, %i, 200));
|
||||
commandToClient(%client, 't2csri_getChallengeChunk', getSubStr($temp, %i, 200));
|
||||
}
|
||||
|
||||
// tell the client we're done sending
|
||||
commandToClient(%client, 't2csri_decryptChallenge');
|
||||
|
||||
|
|
@ -189,7 +191,7 @@ function serverCmdt2csri_sendChallenge(%client, %clientChallenge)
|
|||
{
|
||||
// uh oh... someone's being naughty.. valid cert, but for a different player. kill them!
|
||||
%client.setDisconnectReason("Community supplemental certificate doesn't match account certificate.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -206,10 +208,10 @@ function serverCmdt2csri_challengeResponse(%client, %serverChallenge)
|
|||
if (%client.t2csri_serverChallenge $= %serverChallenge)
|
||||
{
|
||||
// check to see if the client is GUID banned, now that we verified their certificate
|
||||
if (BanList::isBanned(getField(%client.t2csri_authInfo, 3), "*"))
|
||||
if (banList_checkGUID(getField(%client.t2csri_authInfo, 3)))
|
||||
{
|
||||
%client.setDisconnectReason("You are not allowed to play on this server.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -219,7 +221,7 @@ function serverCmdt2csri_challengeResponse(%client, %serverChallenge)
|
|||
else
|
||||
{
|
||||
%client.setDisconnectReason("Invalid server challenge. Check your account key for corruption.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -228,9 +230,8 @@ function t2csri_expireClient(%client)
|
|||
{
|
||||
if (!isObject(%client))
|
||||
return;
|
||||
|
||||
%client.setDisconnectReason("This is a TribesNEXT server. You must install the TribesNEXT client to play. See www.tribesnext.com for info.");
|
||||
%client.schedule(0, delete);
|
||||
%client.setDisconnectReason("This is a TribesNext server. You must install the TribesNext client to play. See www.tribesnext.com for info.");
|
||||
%client.delete();
|
||||
}
|
||||
|
||||
package t2csri_server
|
||||
|
|
@ -238,17 +239,17 @@ package t2csri_server
|
|||
// packaged to create the "pre-connection" authentication phase
|
||||
function GameConnection::onConnect(%client, %name, %raceGender, %skin, %voice, %voicePitch)
|
||||
{
|
||||
if (%client.getAddress() !$= "local" && %client.t2csri_serverChallenge $= "")
|
||||
if (%client.t2csri_serverChallenge $= "" && !%client.isAIControlled() && %client.getAddress() !$= "Local")
|
||||
{
|
||||
// check to see if the client is IP banned
|
||||
if (BanList::isBanned(0, %client.getAddress()))
|
||||
if (banList_checkIP(%client))
|
||||
{
|
||||
%client.setDisconnectReason("You are not allowed to play on this server.");
|
||||
%client.schedule(0, delete);
|
||||
%client.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
// echo("Client connected. Initializing pre-connection authentication phase...");
|
||||
//echo("Client connected. Initializing pre-connection authentication phase...");
|
||||
// save these for later
|
||||
%client.tname = %name;
|
||||
%client.trgen = %raceGender;
|
||||
|
|
@ -259,10 +260,10 @@ package t2csri_server
|
|||
// start the 15 second count down
|
||||
%client.tterm = schedule(15000, 0, t2csri_expireClient, %client);
|
||||
|
||||
commandToClient(%client, 't2csri_pokeClient', "T2CSRI 1.5 - 08/09/2012");
|
||||
commandToClient(%client, 't2csri_pokeClient', "T2CSRI 1.1 - 03/18/2009");
|
||||
return;
|
||||
}
|
||||
// echo("Client completed pre-authentication phase.");
|
||||
//echo("Client completed pre-authentication phase.");
|
||||
|
||||
// continue connection process
|
||||
if (isEventPending(%client.tterm))
|
||||
|
|
@ -270,7 +271,6 @@ package t2csri_server
|
|||
|
||||
Parent::onConnect(%client, %name, %raceGender, %skin, %voice, %voicePitch);
|
||||
%client.doneAuthenticating = 1;
|
||||
%client.t2csri_cert = "";
|
||||
}
|
||||
|
||||
// packaged to prevent game leaving messages for clients that are in the authentication phase
|
||||
|
|
@ -278,7 +278,6 @@ package t2csri_server
|
|||
{
|
||||
if (!isObject(%client) || !%client.doneAuthenticating)
|
||||
return;
|
||||
|
||||
Parent::onDrop(%client, %reason);
|
||||
}
|
||||
|
||||
|
|
@ -292,33 +291,11 @@ package t2csri_server
|
|||
// clan support will be implemented via delegation to a community server
|
||||
function GameConnection::getAuthInfo(%client)
|
||||
{
|
||||
if (%client.t2csri_authInfo $= "" && %client.getAddress() $= "local")
|
||||
if (%client.getAddress() $= "Local" && %client.t2csri_authInfo $= "")
|
||||
%client.t2csri_authInfo = WONGetAuthInfo();
|
||||
|
||||
return %client.t2csri_authInfo;
|
||||
}
|
||||
|
||||
// deactivating old master list server protocol handlers in script
|
||||
// sending a game type list to a dedicated server would result in a massive number
|
||||
// of nuiscance calls to the following functions, and spam the console with pages of errors
|
||||
// the errors were the main source of CPU utilization, so just setting stubs is adequate protection
|
||||
function addGameType()
|
||||
{
|
||||
return;
|
||||
}
|
||||
function clearGameTypes()
|
||||
{
|
||||
return;
|
||||
}
|
||||
function clearMissionTypes()
|
||||
{
|
||||
return;
|
||||
}
|
||||
function sortGameAndMissionTypeLists()
|
||||
{
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if ($PlayingOnline && !isActivePackage(t2csri_server))
|
||||
activatePackage(t2csri_server);
|
||||
if ($PlayingOnline)
|
||||
activatePackage(t2csri_server);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// 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
|
||||
|
|
@ -24,6 +25,16 @@
|
|||
// 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)
|
||||
{
|
||||
|
|
@ -77,6 +88,8 @@ function serverCmdt2csri_finishedDCE(%client)
|
|||
|
||||
if (%sigSha !$= %calcSha)
|
||||
{
|
||||
echo(%sigSha);
|
||||
warn(%calcSha);
|
||||
%client.setDisconnectReason("DCE is not signed by authoritative root.");
|
||||
%client.delete();
|
||||
return;
|
||||
|
|
@ -161,9 +174,10 @@ function serverCmdt2csri_comCertSendDone(%client)
|
|||
}
|
||||
|
||||
// get the signature SHA1
|
||||
%sigSha = rsa_mod_exp(%sig, %e, %n);
|
||||
while (strlen(%sigSha) < 40)
|
||||
%sigSha = "0" @ %sigSha;
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,16 +1,22 @@
|
|||
// 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 initialization and glue file (server side)
|
||||
|
||||
if (isObject(ServerGroup))
|
||||
{
|
||||
// load the Ruby utils and cryptography module
|
||||
exec("t2csri/rubyUtils.cs");
|
||||
rubyExec("t2csri/crypto.rb");
|
||||
|
||||
// load the torque script components
|
||||
exec("t2csri/serverSide.cs");
|
||||
exec("t2csri/serverSideClans.cs");
|
||||
exec("t2csri/bans.cs");
|
||||
exec("t2csri/ipv4.cs");
|
||||
exec("t2csri/base64.cs");
|
||||
|
||||
// get the global IP for sanity testing purposes
|
||||
schedule(32, 0, ipv4_getInetAddress);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue