diff --git a/gts/admin/Sessions.aspx.cs b/gts/admin/Sessions.aspx.cs index 2b3d5a6b..7972347a 100644 --- a/gts/admin/Sessions.aspx.cs +++ b/gts/admin/Sessions.aspx.cs @@ -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("):
"); - foreach (KeyValuePair session in manager.Sessions4) - { - builder.Append("PID: "); - builder.Append(session.Value.PID); - builder.Append("
Token: "); - builder.Append(session.Value.Token); - builder.Append("
Hash: "); - builder.Append(session.Value.Hash); - builder.Append("
URL: "); - builder.Append(session.Value.URL); - builder.Append("
Expires: "); - builder.Append(session.Value.ExpiryDate); - builder.Append("

"); - } - - builder.Append("Active GenV sessions ("); - builder.Append(manager.Sessions5.Count); - builder.Append("):
"); - foreach (KeyValuePair session in manager.Sessions5) - { - builder.Append("PID: "); - builder.Append(session.Value.PID); - builder.Append("
Token: "); - builder.Append(session.Value.Token); - builder.Append("
Hash: "); - builder.Append(session.Value.Hash); - builder.Append("
URL: "); - builder.Append(session.Value.URL); - builder.Append("
Expires: "); - builder.Append(session.Value.ExpiryDate); - builder.Append("

"); - } GamestatsSessionManager gsm = GamestatsSessionManager.FromContext(Context); - builder.Append("Active GSM sessions ("); + builder.Append("Active sessions ("); builder.Append(gsm.Sessions.Count); builder.Append("):
"); foreach (KeyValuePair session in gsm.Sessions) @@ -73,7 +36,6 @@ namespace PkmnFoundations.GTS.admin builder.Append("

"); } - litDebug.Text = builder.ToString(); } } diff --git a/gts/gts.csproj b/gts/gts.csproj index 62a55543..f02780a1 100644 --- a/gts/gts.csproj +++ b/gts/gts.csproj @@ -3544,7 +3544,6 @@ RoomLeaders.aspx - AddDressup.aspx @@ -3588,9 +3587,6 @@ pokemondpds.ashx - - - Default.aspx ASPXCodeBehind @@ -3609,7 +3605,6 @@ MasterPage.master - VideoId.aspx ASPXCodeBehind diff --git a/gts/masters/MasterPage.master.cs b/gts/masters/MasterPage.master.cs index e4365686..93656716 100644 --- a/gts/masters/MasterPage.master.cs +++ b/gts/masters/MasterPage.master.cs @@ -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; diff --git a/gts/src/GamestatsSessionPlat.cs b/gts/src/GamestatsSessionPlat.cs deleted file mode 100644 index 5a153af0..00000000 --- a/gts/src/GamestatsSessionPlat.cs +++ /dev/null @@ -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(); - } - - /// - /// 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. - /// - 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(); - } - } -} diff --git a/gts/src/GtsSession4.cs b/gts/src/GtsSession4.cs deleted file mode 100644 index 01f742ca..00000000 --- a/gts/src/GtsSession4.cs +++ /dev/null @@ -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(); - } - - /// - /// 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. - /// - 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; - } - } -} diff --git a/gts/src/GtsSession5.cs b/gts/src/GtsSession5.cs deleted file mode 100644 index 4934245d..00000000 --- a/gts/src/GtsSession5.cs +++ /dev/null @@ -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(); - } - - /// - /// 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. - /// - 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(); - } - } -} diff --git a/gts/src/GtsSessionBase.cs b/gts/src/GtsSessionBase.cs deleted file mode 100644 index 7ec3f853..00000000 --- a/gts/src/GtsSessionBase.cs +++ /dev/null @@ -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(); - } - - /// - /// A PID is a user ID which is associated with a game cartridge. - /// - public int PID - { - get - { - return m_pid; - } - protected set - { - m_pid = value; - } - } - - /// - /// The URL in which this session began - /// - public String URL - { - get - { - return m_url; - } - protected set - { - m_url = value; - } - } - - /// - /// 32 chars of random data which both functions as a challenge - /// and as a session ID - /// - 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('/', '_'); - } - } -} diff --git a/gts/src/GtsSessionManager.cs b/gts/src/GtsSessionManager.cs deleted file mode 100644 index e07320d7..00000000 --- a/gts/src/GtsSessionManager.cs +++ /dev/null @@ -1,165 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using PkmnFoundations.Data; - -namespace PkmnFoundations.GTS -{ - /// - /// Contains collections of GTS sessions (both generations) - /// and some indexes for fast lookup - /// - public class GtsSessionManager - { - public readonly Dictionary Sessions4; - public readonly Dictionary Sessions5; - - public GtsSessionManager() - { - Sessions4 = new Dictionary(); - Sessions5 = new Dictionary(); - RefreshStats(); - } - - public void PruneSessions() - { - PruneSessions4(); - PruneSessions5(); - } - - public void PruneSessions4() - { - Dictionary sessions = Sessions4; - DateTime now = DateTime.UtcNow; - - lock (sessions) - { - Queue toRemove = new Queue(); - foreach (KeyValuePair session in sessions) - { - if (session.Value.ExpiryDate < now) toRemove.Enqueue(session.Key); - } - while (toRemove.Count > 0) - { - sessions.Remove(toRemove.Dequeue()); - } - } - } - - public void PruneSessions5() - { - Dictionary sessions = Sessions5; - DateTime now = DateTime.UtcNow; - - lock (sessions) - { - Queue toRemove = new Queue(); - foreach (KeyValuePair 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); - } - - /// - /// Retrieves the GtsSessionManager from the context's application state. - /// - 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; - } - - } -} \ No newline at end of file diff --git a/gts/test/Decode.aspx.cs b/gts/test/Decode.aspx.cs index cf3e3b20..01b92887 100644 --- a/gts/test/Decode.aspx.cs +++ b/gts/test/Decode.aspx.cs @@ -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);