diff options
| author | Darkelarious <darkelarious@333networks.com> | 2015-02-09 07:58:06 +0100 |
|---|---|---|
| committer | Darkelarious <darkelarious@333networks.com> | 2015-02-09 07:58:06 +0100 |
| commit | 5057ec47aa9a1702b2483e0a0b3ba325bb0b7abb (patch) | |
| tree | b1a394f64ec7ec2cf69f33bceb54a5b51199c0a4 /lib/MasterServer/Core/Secure.pm | |
| parent | 6ac9d390e417b868b6ed79441b8cc6e1b2ebeb13 (diff) | |
| download | MasterServer-Perl-5057ec47aa9a1702b2483e0a0b3ba325bb0b7abb.tar.gz MasterServer-Perl-5057ec47aa9a1702b2483e0a0b3ba325bb0b7abb.zip | |
receive UDP beacons, validate them and store with country indicator
Diffstat (limited to 'lib/MasterServer/Core/Secure.pm')
| -rwxr-xr-x | lib/MasterServer/Core/Secure.pm | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/lib/MasterServer/Core/Secure.pm b/lib/MasterServer/Core/Secure.pm new file mode 100755 index 0000000..dbe9c1f --- /dev/null +++ b/lib/MasterServer/Core/Secure.pm @@ -0,0 +1,210 @@ + +package MasterServer::Core::Secure; + +use strict; +use warnings; +use POSIX qw/strftime/; +use Exporter 'import'; + +our @EXPORT = qw| secure_string validated_beacon validated_request validate_string charshift get_validate_string|; + +## generate a random string of 6 characters long for the \secure\ challenge +sub secure_string { + # spit out a random string, only uppercase characters + my @c = ('A'..'Z'); + my $s = ""; + $s .= $c[rand @c] for 1..6; + + # return random string + return $s; +} + +## Check if beacon has a valid response. +sub validated_beacon { + my ($self, $gamename, $secure, $enctype, $validate) = @_; + + # debugging enabled? Then don't care about validation + return 1 if ($self->{debug_validate}); + + # enctype given? + $enctype = 0 unless $enctype; + + if ($self->{ignore_beacon_key} =~ m/$gamename/i){ + $self->log("secure", "Ignored beacon validation for $gamename."); + return 1; + } + + # compare received response with challenge + return ($self->validate_string($gamename, $secure, $enctype) eq $validate) || 0; +} + +## Check if request has valid response +sub validated_request { + my ($self, $gamename, $secure, $enctype, $validate) = @_; + + # debugging enabled? Then don't care about validation + return 1 if ($self->{debug_validate}); + + # enctype given? + $enctype = 0 unless $enctype; + + # ignore games and beacons that are listed + if ($self->{ignore_browser_key} =~ m/$gamename/i){ + $self->log("secure", "Ignored browser validation for $gamename."); + return 1; + } + + # compare received response with challenge + return ($self->validate_string($gamename, $secure, $enctype) eq $validate) || 0; +} + +################################################################################ +# calculate the \validate\ response for the \secure\ challenge. +# args: gamename, secure_string, encryption type +# returns: validate string (usually 8 characters long) +# !! requires cipher hash to be configured in config! (imported or else) +################################################################################ +sub validate_string { + my ($self, $game, $sec, $enc) = @_; + + # get cipher from gamename + my $cip = $self->{game}->{$game}->{key} || "XXXXXX"; + + # don't accept challenge longer than 16 characters -- usually h@xx0rs + if (length $sec > 16) { + return "0"} + + # check for valid encryption choises + my $enc_val = (defined $enc && 0 <= $enc && $enc <= 2) ? $enc : 0; + + # calculate and return validate string + return $self->get_validate_string($cip, $sec, $enc_val); +} + +################################################################################ +# rotate characters as part of the secure/validate algorithm. +# arg and return: int (representing a character) +################################################################################ +sub charshift { + my ($self, $reg) = @_; + return($reg + 65) if ($reg < 26); + return($reg + 71) if ($reg < 52); + return($reg - 4) if ($reg < 62); + return(43) if ($reg == 62); + return(47) if ($reg == 63); + + # if all else fails + return(0); +} + +################################################################################ +# algorithm to calculate the response to the secure/validate query. processes +# the secure_string and returns the challenge_string with which GameSpy secure +# protocol authenticates games. +# +# the following algorithm is based on gsmsalg.h in GSMSALG 0.3.3 by Luigi +# Auriemma, aluigi@autistici.org, aluigi.org, copyright 2004-2008. GSMSALG 0.3.3 +# was released under the GNU General Public License, for more information, see +# the original software at http://aluigi.altervista.org/papers.htm#gsmsalg +# +# conversion and modification of the algorithm by Darkelarious, June 2014. +# +# args: game cipher, 6-char challenge string, encryption type +# returns: validate string (usually 8 characters long) +# !! requires cipher hash to be configured in config! (imported or else) +################################################################################ +sub get_validate_string { + my ($self, $cipher_string, $secure_string, $enctype) = @_; + + # import pre-built rotations from config for enctype + # -- see GSMSALG 0.3.3 reference for copyright and more information + my @enc_chars = $self->{enc_chars}; + + # convert to array of characters + my @cip = split "", $cipher_string; + my @sec = split "", $secure_string; + + # length of strings/arrays which should be 6 + my $sec_len = scalar @sec; + my $cip_len = scalar @cip; + + # from this point on, work with ordinal values + for (0..$sec_len-1) { $sec[$_] = ord $sec[$_]; } + for (0..$cip_len-1) { $cip[$_] = ord $cip[$_]; } + + # helper vars + my ($i,$j,$k,$l,$m,$n,$p); + + # too short or too long -- return empty string + return "" if ($sec_len <= 0 || $sec_len >= 32); + return "" if ($cip_len <= 0 || $cip_len >= 32); + + # temporary array with ascii characters + my @enc; + for(0..255) {$enc[$_] = $_;} + + $j = 0; + for(0..255) { + $j += $enc[$_] + $cip[$_ % $cip_len]; + $j = $j % 256; + $l = $enc[$j]; + $enc[$j] = $enc[$_]; + $enc[$_] = $l; + } + + # store temporary positions + my @tmp; + + $j = 0; + $k = 0; + for($i = 0; $sec[$i]; $i++) { + $j += $sec[$i] + 1; + $j = $j % 256; + $l = $enc[$j]; + $k += $l; + $k = $k % 256; + $m = $enc[$k]; + $enc[$k] = $l; + $enc[$j] = $m; + $tmp[$i] = $sec[$i] ^ $enc[($l + $m) & 0xff]; + } + +# part of the enctype 1-2 process + for($sec_len = $i; $sec_len % 3; $sec_len++) { + $tmp[$sec_len] = 0; + } + + if ($enctype == 1) { + for (0..$sec_len-1) { + $tmp[$_] = $enc_chars[$tmp[$_]]; + } + } + elsif ($enctype == 2) { + for (0..$sec_len-1) { + $tmp[$_] ^= $cip[$_ % $cip_len]; + } + } + + # parse the validate array + $p = 0; + my @val; + for($i = 0; $i < $sec_len; $i += 3) { + $l = $tmp[$i]; + $m = $tmp[$i + 1]; + $m = $m % 256; + $n = $tmp[$i + 2]; + $n = $n % 256; + $val[$p++] = $self->charshift($l >> 2); + $val[$p++] = $self->charshift((($l & 3 ) << 4) | ($m >> 4)); + $val[$p++] = $self->charshift((($m & 15) << 2) | ($n >> 6)); + $val[$p++] = $self->charshift($n & 63); + } + + # return to ascii characters + my $str = ""; + for (@val) { $str .= chr $_} + + return $str; +} + +1; |
