Removed obsolete gamestats session classes.

This commit is contained in:
Greg Edwards
2015-01-23 20:08:57 -05:00
parent bdbadfe811
commit 9c4682033e
9 changed files with 16 additions and 612 deletions

View File

@@ -13,47 +13,10 @@ namespace PkmnFoundations.GTS.admin
{
protected void Page_Load(object sender, EventArgs e)
{
GtsSessionManager manager = GtsSessionManager.FromContext(Context);
StringBuilder builder = new StringBuilder();
builder.Append("Active GenIV sessions (");
builder.Append(manager.Sessions4.Count);
builder.Append("):<br />");
foreach (KeyValuePair<String, GtsSession4> session in manager.Sessions4)
{
builder.Append("PID: ");
builder.Append(session.Value.PID);
builder.Append("<br />Token: ");
builder.Append(session.Value.Token);
builder.Append("<br />Hash: ");
builder.Append(session.Value.Hash);
builder.Append("<br />URL: ");
builder.Append(session.Value.URL);
builder.Append("<br />Expires: ");
builder.Append(session.Value.ExpiryDate);
builder.Append("<br /><br />");
}
builder.Append("Active GenV sessions (");
builder.Append(manager.Sessions5.Count);
builder.Append("):<br />");
foreach (KeyValuePair<String, GtsSession5> session in manager.Sessions5)
{
builder.Append("PID: ");
builder.Append(session.Value.PID);
builder.Append("<br />Token: ");
builder.Append(session.Value.Token);
builder.Append("<br />Hash: ");
builder.Append(session.Value.Hash);
builder.Append("<br />URL: ");
builder.Append(session.Value.URL);
builder.Append("<br />Expires: ");
builder.Append(session.Value.ExpiryDate);
builder.Append("<br /><br />");
}
GamestatsSessionManager gsm = GamestatsSessionManager.FromContext(Context);
builder.Append("Active GSM sessions (");
builder.Append("Active sessions (");
builder.Append(gsm.Sessions.Count);
builder.Append("):<br />");
foreach (KeyValuePair<String, GamestatsSession> session in gsm.Sessions)
@@ -73,7 +36,6 @@ namespace PkmnFoundations.GTS.admin
builder.Append("<br /><br />");
}
litDebug.Text = builder.ToString();
}
}

View File

@@ -3544,7 +3544,6 @@
<DependentUpon>RoomLeaders.aspx</DependentUpon>
</Compile>
<Compile Include="src\DependencyNode.cs" />
<Compile Include="src\GamestatsSessionPlat.cs" />
<Compile Include="src\HeaderColour.cs" />
<Compile Include="admin\AddDressup.aspx.cs">
<DependentUpon>AddDressup.aspx</DependentUpon>
@@ -3588,9 +3587,6 @@
<DependentUpon>pokemondpds.ashx</DependentUpon>
</Compile>
<Compile Include="src\Common.cs" />
<Compile Include="src\GtsSession5.cs" />
<Compile Include="src\GtsSession4.cs" />
<Compile Include="src\GtsSessionBase.cs" />
<Compile Include="Default.aspx.cs">
<DependentUpon>Default.aspx</DependentUpon>
<SubType>ASPXCodeBehind</SubType>
@@ -3609,7 +3605,6 @@
<DependentUpon>MasterPage.master</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="src\GtsSessionManager.cs" />
<Compile Include="test\VideoId.aspx.cs">
<DependentUpon>VideoId.aspx</DependentUpon>
<SubType>ASPXCodeBehind</SubType>

View File

@@ -12,12 +12,8 @@ namespace PkmnFoundations.GTS
{
protected void Page_Load(object sender, EventArgs e)
{
// todo: I should store the available pokemon count
// on the cache in the same way so the website doesn't
// have a dependency on running in the same application
// as the gamestats server.
GtsSessionManager manager = GtsSessionManager.FromContext(Context);
int availTotal = manager.AvailablePokemon4 + manager.AvailablePokemon5;
// todo: Cache this
int availTotal = Database.Instance.GtsAvailablePokemon4() + Database.Instance.GtsAvailablePokemon5();
litPokemon.Text = availTotal.ToString();
ulong bvCount4, bvCount5;

View File

@@ -1,94 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Cryptography;
using System.IO;
using System.Net;
using GamestatsBase;
namespace PkmnFoundations.GTS
{
public class GamestatsSessionPlat : GtsSessionBase
{
public GamestatsSessionPlat(int pid, String url)
: base(pid, url)
{
Hash = ComputeHash(Token);
}
public static String ComputeHash(String token)
{
// todo: add unit tests with some wiresharked examples.
if (m_sha1 == null) m_sha1 = SHA1.Create();
String longToken = "uLMOGEiiJogofchScpXb" + token;
byte[] data = new byte[longToken.Length];
MemoryStream stream = new MemoryStream(data);
StreamWriter writer = new StreamWriter(stream);
writer.Write(longToken); // fixme: this throws an OutOfBoundsException if the passed token contains non-ascii.
writer.Flush();
return m_sha1.ComputeHash(data).ToHexStringLower();
}
/// <summary>
/// Decrypts the NDS &data= querystring into readable binary data.
/// The PID (little endian) is left at the start of the output
/// but the (unencrypted) checksum is removed.
/// </summary>
public static byte[] DecryptData(String data)
{
byte[] data2 = FromUrlSafeBase64String(data);
if (data2.Length < 4) throw new FormatException("Data must contain at least 4 bytes.");
byte[] data3 = new byte[data2.Length - 4];
int checksum = BitConverter.ToInt32(data2, 0);
checksum = IPAddress.NetworkToHostOrder(checksum); // endian flip
// overlay_0066.bin offset 0x2b2bc contains the evil xor mask I've been looking for,
// encoded ... in ASCII... >__< (I was looking for binary)
// uLMOGEiiJogofchScpXb000244fd00006015100000005b440e7epokemondpds
checksum ^= 0x5b440e7e;
int rand = checksum | (checksum << 16);
// todo: prune first 8 bytes, pass pid to this function to validate
for (int pos = 0; pos < data3.Length; pos++)
{
rand = DecryptRNG(rand);
data3[pos] = (byte)(data2[pos + 4] ^ (byte)(rand >> 16));
}
// todo: validate and prune length, as in BW
int checkedsum = 0;
foreach (byte b in data3)
checkedsum += b;
if (checkedsum != checksum) throw new FormatException("Data checksum is incorrect.");
return data3;
}
private static int DecryptRNG(int prev)
{
return (prev * 0x000244fd + 0x00006015) & ~0x10000000;
}
public static String ResponseChecksum(byte[] responseArray)
{
if (m_sha1 == null) m_sha1 = SHA1.Create();
String toCheck = "uLMOGEiiJogofchScpXb" + ToUrlSafeBase64String(responseArray) + "uLMOGEiiJogofchScpXb";
byte[] data = new byte[toCheck.Length];
MemoryStream stream = new MemoryStream(data);
StreamWriter writer = new StreamWriter(stream);
writer.Write(toCheck);
writer.Flush();
return m_sha1.ComputeHash(data).ToHexStringLower();
}
}
}

View File

@@ -1,77 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Cryptography;
using System.IO;
using System.Net;
using GamestatsBase;
namespace PkmnFoundations.GTS
{
public class GtsSession4 : GtsSessionBase
{
public GtsSession4(int pid, String url) : base(pid, url)
{
Hash = ComputeHash(Token);
}
public static String ComputeHash(String token)
{
// todo: add unit tests with some wiresharked examples.
// examples: (token -> hash)
// bTII8cU1Kx86cTZPhEqXqLivqpRUUVpU -> 72bd08d60755b572da85d714508d14e68596bbdc
// 7BEqRQlKsqRh8wTdL3rfgFxu053pgPzO -> e1c8f8b5b4cc0f1062cf215b1e7f36afed124f1e
// 081PAAfk5SQhC7LTu1Iq7mwGtQ77xPOR -> ef8321dbb5b562d0bf2f3342008a1ba53851a14f
if (m_sha1 == null) m_sha1 = SHA1.Create();
String longToken = "sAdeqWo3voLeC5r16DYv" + token;
byte[] data = new byte[longToken.Length];
MemoryStream stream = new MemoryStream(data);
StreamWriter writer = new StreamWriter(stream);
writer.Write(longToken); // fixme: this throws an OutOfBoundsException if the passed token contains non-ascii.
writer.Flush();
return m_sha1.ComputeHash(data).ToHexStringLower();
}
/// <summary>
/// Decrypts the NDS &data= querystring into readable binary data.
/// The PID (little endian) is left at the start of the output
/// but the (unencrypted) checksum is removed.
/// </summary>
public static byte[] DecryptData(String data)
{
byte[] data2 = FromUrlSafeBase64String(data);
if (data2.Length < 4) throw new FormatException("Data must contain at least 4 bytes.");
byte[] data3 = new byte[data2.Length - 4];
int checksum = BitConverter.ToInt32(data2, 0);
checksum = IPAddress.NetworkToHostOrder(checksum); // endian flip
checksum ^= 0x4a3b2c1d;
int rand = checksum | (checksum << 16);
// todo: prune first 8 bytes, pass pid to this function to validate
for (int pos = 0; pos < data3.Length; pos++)
{
rand = DecryptRNG(rand);
data3[pos] = (byte)(data2[pos + 4] ^ (byte)(rand >> 16));
}
int checkedsum = 0;
foreach (byte b in data3)
checkedsum += b;
if (checkedsum != checksum) throw new FormatException("Data checksum is incorrect.");
return data3;
}
private static int DecryptRNG(int prev)
{
return (prev * 0x45 + 0x1111) & 0x7fffffff;
}
}
}

View File

@@ -1,84 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Cryptography;
using System.IO;
using System.Net;
using GamestatsBase;
namespace PkmnFoundations.GTS
{
public class GtsSession5 : GtsSessionBase
{
public GtsSession5(int pid, String url) : base(pid, url)
{
Hash = ComputeHash(Token);
}
public static String ComputeHash(String token)
{
// todo: add unit tests with some wiresharked examples.
if (m_sha1 == null) m_sha1 = SHA1.Create();
String longToken = "HZEdGCzcGGLvguqUEKQN" + token;
byte[] data = new byte[longToken.Length];
MemoryStream stream = new MemoryStream(data);
StreamWriter writer = new StreamWriter(stream);
writer.Write(longToken); // fixme: this throws an OutOfBoundsException if the passed token contains non-ascii.
writer.Flush();
return m_sha1.ComputeHash(data).ToHexStringLower();
}
/// <summary>
/// Decrypts the NDS &data= querystring into readable binary data.
/// The PID (little endian) is left at the start of the output
/// but the (unencrypted) checksum is removed.
/// </summary>
public static byte[] DecryptData(String data)
{
byte[] data2 = FromUrlSafeBase64String(data);
if (data2.Length < 12) throw new FormatException("Data must contain at least 12 bytes.");
byte[] data3 = new byte[data2.Length - 4];
int checksum = BitConverter.ToInt32(data2, 0);
checksum = IPAddress.NetworkToHostOrder(checksum); // endian flip
checksum ^= 0x2db842b2;
int length = BitConverter.ToInt32(data2, 8);
// prune the checksum but keep pid and length.
// this maximizes similarity with genIV's and allows
// the ashx to check for pids to match.
// todo: prune first 12 bytes, pass pid to this function to validate
Array.Copy(data2, 4, data3, 0, data2.Length - 4);
if (length + 8 != data3.Length) throw new FormatException("Data length is incorrect.");
int checkedsum = 0;
foreach (byte b in data3)
checkedsum += b;
if (checkedsum != checksum) throw new FormatException("Data checksum is incorrect.");
return data3;
}
public static String ResponseChecksum(byte[] responseArray)
{
if (m_sha1 == null) m_sha1 = SHA1.Create();
String toCheck = "HZEdGCzcGGLvguqUEKQN" + ToUrlSafeBase64String(responseArray) + "HZEdGCzcGGLvguqUEKQN";
byte[] data = new byte[toCheck.Length];
MemoryStream stream = new MemoryStream(data);
StreamWriter writer = new StreamWriter(stream);
writer.Write(toCheck);
writer.Flush();
return m_sha1.ComputeHash(data).ToHexStringLower();
}
}
}

View File

@@ -1,137 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Cryptography;
namespace PkmnFoundations.GTS
{
public class GtsSessionBase
{
protected static RNGCryptoServiceProvider m_rng;
protected static SHA1 m_sha1;
private int m_pid;
private String m_url;
private String m_token;
private String m_hash;
private DateTime m_expiry_date;
protected GtsSessionBase(int pid, String url)
{
PID = pid;
URL = url;
ExpiryDate = DateTime.UtcNow.AddMinutes(10);
Token = CreateToken();
}
/// <summary>
/// A PID is a user ID which is associated with a game cartridge.
/// </summary>
public int PID
{
get
{
return m_pid;
}
protected set
{
m_pid = value;
}
}
/// <summary>
/// The URL in which this session began
/// </summary>
public String URL
{
get
{
return m_url;
}
protected set
{
m_url = value;
}
}
/// <summary>
/// 32 chars of random data which both functions as a challenge
/// and as a session ID
/// </summary>
public String Token
{
get
{
return m_token;
}
protected set
{
m_token = value;
}
}
public String Hash
{
get
{
return m_hash;
}
protected set
{
m_hash = value;
}
}
public DateTime ExpiryDate
{
get
{
return m_expiry_date;
}
protected set
{
m_expiry_date = value;
}
}
public object Tag
{
get;
set;
}
public static String CreateToken()
{
if (m_rng == null) m_rng = new RNGCryptoServiceProvider();
char[] token = new char[32];
byte[] data = new byte[4];
for (int x = 0; x < token.Length; x++)
{
// tokens have 62 possible chars: 0-9, A-Z, and a-z
m_rng.GetBytes(data);
uint rand = BitConverter.ToUInt32(data, 0) % 62u;
if (rand < 10)
token[x] = (char)('0' + rand);
else if (rand < 36)
token[x] = (char)('7' + rand); // 'A' + rand - 10
else
token[x] = (char)('=' + rand); // 'a' + rand - 36
}
return new String(token);
}
public static byte[] FromUrlSafeBase64String(String data)
{
return Convert.FromBase64String(data.Replace('-', '+').Replace('_', '/'));
}
public static String ToUrlSafeBase64String(byte[] data)
{
return Convert.ToBase64String(data).Replace('+', '-').Replace('/', '_');
}
}
}

View File

@@ -1,165 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using PkmnFoundations.Data;
namespace PkmnFoundations.GTS
{
/// <summary>
/// Contains collections of GTS sessions (both generations)
/// and some indexes for fast lookup
/// </summary>
public class GtsSessionManager
{
public readonly Dictionary<String, GtsSession4> Sessions4;
public readonly Dictionary<String, GtsSession5> Sessions5;
public GtsSessionManager()
{
Sessions4 = new Dictionary<String, GtsSession4>();
Sessions5 = new Dictionary<String, GtsSession5>();
RefreshStats();
}
public void PruneSessions()
{
PruneSessions4();
PruneSessions5();
}
public void PruneSessions4()
{
Dictionary<String, GtsSession4> sessions = Sessions4;
DateTime now = DateTime.UtcNow;
lock (sessions)
{
Queue<String> toRemove = new Queue<String>();
foreach (KeyValuePair<String, GtsSession4> session in sessions)
{
if (session.Value.ExpiryDate < now) toRemove.Enqueue(session.Key);
}
while (toRemove.Count > 0)
{
sessions.Remove(toRemove.Dequeue());
}
}
}
public void PruneSessions5()
{
Dictionary<String, GtsSession5> sessions = Sessions5;
DateTime now = DateTime.UtcNow;
lock (sessions)
{
Queue<String> toRemove = new Queue<String>();
foreach (KeyValuePair<String, GtsSession5> session in sessions)
{
if (session.Value.ExpiryDate < now) toRemove.Enqueue(session.Key);
}
while (toRemove.Count > 0)
{
sessions.Remove(toRemove.Dequeue());
}
}
}
public void Add(GtsSession4 session)
{
Sessions4.Add(session.Hash, session);
}
public void Add(GtsSession5 session)
{
Sessions5.Add(session.Hash, session);
}
public void Remove(GtsSession4 session)
{
Sessions4.Remove(session.Hash);
}
public void Remove(GtsSession5 session)
{
Sessions5.Remove(session.Hash);
}
/// <summary>
/// Retrieves the GtsSessionManager from the context's application state.
/// </summary>
public static GtsSessionManager FromContext(HttpContext context)
{
object oManager = context.Application["GtsSessionManager"];
GtsSessionManager manager = oManager as GtsSessionManager;
if (manager == null)
{
manager = new GtsSessionManager();
context.Application.Add("GtsSessionManager", manager);
}
return manager;
}
public GtsSession4 FindSession4(int pid, String url)
{
// todo: keep a hash table of pids that maps them onto session objects
GtsSession4 result = null;
foreach (GtsSession4 sess in Sessions4.Values)
{
if (sess.PID == pid && sess.URL == url)
{
if (result != null)
{
// todo: there's more than one matching session... delete them all.
}
return sess; // temp until I get it to cleanup old sessions
result = sess;
}
}
return result;
}
public GtsSession5 FindSession5(int pid, String url)
{
// todo: keep a hash table of pids that maps them onto session objects
GtsSession5 result = null;
foreach (GtsSession5 sess in Sessions5.Values)
{
if (sess.PID == pid && sess.URL == url)
{
if (result != null)
{
// todo: there's more than one matching session... delete them all.
}
return sess; // temp until I get it to cleanup old sessions
result = sess;
}
}
return result;
}
public void RefreshStats()
{
AvailablePokemon4 = Database.Instance.GtsAvailablePokemon4();
AvailablePokemon5 = Database.Instance.GtsAvailablePokemon5();
}
public int AvailablePokemon4
{
get;
private set;
}
public int AvailablePokemon5
{
get;
private set;
}
}
}

View File

@@ -26,7 +26,11 @@ namespace PkmnFoundations.GTS.debug
try
{
data = GtsSession4.DecryptData(txtData.Text);
GamestatsHandler gs4 = new GamestatsHandler("sAdeqWo3voLeC5r16DYv",
0x45, 0x1111, 0x80000000, 0x4a3b2c1d, "pokemondpds",
GamestatsRequestVersions.Version2, GamestatsResponseVersions.Version1, true);
data = gs4.DecryptData(txtData.Text);
litGeneration.Text = "4";
}
catch (FormatException)
@@ -35,7 +39,9 @@ namespace PkmnFoundations.GTS.debug
if (data == null) try
{
data = GtsSession5.DecryptData(txtData.Text);
GamestatsHandler gs5 = new GamestatsHandler("HZEdGCzcGGLvguqUEKQN0001d93500002dd5000000082db842b2syachi2ds",
GamestatsRequestVersions.Version3, GamestatsResponseVersions.Version2, false);
data = gs5.DecryptData(txtData.Text);
litGeneration.Text = "5";
}
catch (FormatException)
@@ -44,7 +50,9 @@ namespace PkmnFoundations.GTS.debug
if (data == null) try
{
data = GamestatsSessionPlat.DecryptData(txtData.Text);
GamestatsHandler gsPlat = new GamestatsHandler("uLMOGEiiJogofchScpXb000244fd00006015100000005b440e7epokemondpds",
GamestatsRequestVersions.Version3, GamestatsResponseVersions.Version2, true);
data = gsPlat.DecryptData(txtData.Text);
litGeneration.Text = "Platinum";
}
catch (FormatException)
@@ -69,7 +77,7 @@ namespace PkmnFoundations.GTS.debug
if (data == null) try
{
data = GtsSessionBase.FromUrlSafeBase64String(txtData.Text);
data = GamestatsHandler.FromUrlSafeBase64String(txtData.Text);
litGeneration.Text = "Unknown (are you sure this is gamestats data?)";
litChecksum.Text = "";
@@ -93,7 +101,7 @@ namespace PkmnFoundations.GTS.debug
public static byte[] DecryptData(String data)
{
byte[] data2 = GtsSessionBase.FromUrlSafeBase64String(data);
byte[] data2 = GamestatsHandler.FromUrlSafeBase64String(data);
if (data2.Length < 12) throw new FormatException("Data must contain at least 12 bytes.");
int checksum = BitConverter.ToInt32(data2, 0);