diff --git a/client/error.html b/client/error.html index ece9cb2..595db3e 100644 --- a/client/error.html +++ b/client/error.html @@ -34,7 +34,7 @@ @media screen and (max-width: 480px) { .content { - width: 75%; + width: 100%; } } diff --git a/client/index.html b/client/index.html index 1131df8..3b8cefd 100644 --- a/client/index.html +++ b/client/index.html @@ -12,7 +12,7 @@ diff --git a/client/src/def/MD5.d.ts b/client/src/def/MD5.d.ts deleted file mode 100644 index a250a71..0000000 --- a/client/src/def/MD5.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare function md5(input: string, key?: string, raw?: boolean); \ No newline at end of file diff --git a/protocol.md b/protocol.md index d1505dd..1c93c3f 100644 --- a/protocol.md +++ b/protocol.md @@ -1,3 +1,12 @@ + + # PROTOCOL DEFINITION Messages communicated between the client and server follow the same format, but have different meanings depending on which end is the recipient. A message's intent is determined by its packet ID, a unique identifier that tells the client or server how it should react to the received message. A message id that incites bidirectional communication between the client and server should typically be associated with the same message id on the client as on the server, so as to avoid confusion. @@ -29,66 +38,227 @@ All numbers, unless otherwise specified, are the string representation of a base * User IDs: 8 bytes, integer, unsigned * Co-ordinates: 8 bytes, double-precision float +* Big Int: Hex string, variable size ### Packet IDs -A packet ID may have a specific "direction" of communication, in that an endpoint may either act as a _requester_ or a _responder_. A _requester_ is an endpoint that drives all of the communication on that specific packet ID, while the _responder_ is responsible for providing a timely response to the requests. A _responder_ for a specific packet ID should never send that packet ID unsolicited; either the packet will be ignored or the other endpoint will close the connect. Any packet ID marked as bidirectional may be initiated by either endpoint at any time. +A packet ID may have a specific "direction" of communication, in that an endpoint may either act as a _requester_ or a _responder_. A _requester_ is an endpoint that drives all of the communication on that specific packet ID, while the _responder_ is responsible for providing a timely response to the requests it receives. A _responder_ for a specific packet ID should never send that packet ID unsolicited; either the packet will be ignored or the other endpoint will close the connection. Any packet ID marked as bidirectional may be initiated by either endpoint at any time. #### Server to Client - - -
-
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ ID 0: Key Exchange
+ Requester +
#RegionType
1GeneratorBig Int
2ModulusBig Int
3Server KeyBig Int
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ ID 1: Login Attempt
+ [Encrypted] Responder +
#RegionType
1Check ConstString
2SucceededBoolean
3MessageString
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ ID 2: Registration Attempt
+ [Encrypted] Responder +
#RegionType
1Check ConstString
2SucceededBoolean
3MessageString
+
#### Client to Server - - - - - - - - - - - - - - - -
-
- ID 0: Key Exchange
- Responder -
-
#RegionType
1
+
+ + + + + + + + + + + + + + +
+ ID 0: Key Exchange
+ Responder +
#RegionType
1Client KeyBig Int
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ ID 1: Login Attempt
+ [Encrypted] Requester +
#RegionType
1Check ConstString
2UsernameString
3PasswordString
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ID 2: Registration Attempt
+ [Encrypted] Requester +
#RegionType
1Check ConstString
2UsernameString
3PasswordString
4EmailString
+
## Sockstamps diff --git a/server/CircleScape.csproj b/server/CircleScape.csproj index 6d88d5d..5ed0844 100644 --- a/server/CircleScape.csproj +++ b/server/CircleScape.csproj @@ -99,6 +99,7 @@ + diff --git a/server/Encryption/KeyExchange.cs b/server/Encryption/KeyExchange.cs index d0cb98b..f043590 100644 --- a/server/Encryption/KeyExchange.cs +++ b/server/Encryption/KeyExchange.cs @@ -18,6 +18,9 @@ namespace CircleScape.Encryption { Modulus = RNG.NextPrime(512 / 8); } + public Packet GenerateInitialPacket() { + return + } } } diff --git a/server/Entrypoint.cs b/server/Entrypoint.cs index 5af27a1..3fe0de4 100644 --- a/server/Entrypoint.cs +++ b/server/Entrypoint.cs @@ -11,9 +11,6 @@ using Square; namespace CircleScape { class Entrypoint { static void Main(string[] args) { - var a = Square.RNG.NextPrime(512 / 8); - Console.WriteLine(a.ToString("X")); - var server = new Kneesocks.Server(6770, PoolManager.Pending); server.Start(); diff --git a/server/Libraries/Kneesocks/Frame.cs b/server/Libraries/Kneesocks/Frame.cs index 11f808b..77e6279 100644 --- a/server/Libraries/Kneesocks/Frame.cs +++ b/server/Libraries/Kneesocks/Frame.cs @@ -69,12 +69,12 @@ namespace Kneesocks { var headerLengthFirstByte = (byte)Content.Length; if(bodySize >= 0x7E && bodySize <= 0xFFFF) { headerSize += 2; - headerLengthFirstByte = 126; + headerLengthFirstByte = 0x7e; } else if(bodySize > 0xFFFF) { headerSize += 8; - headerLengthFirstByte = 127; + headerLengthFirstByte = 0x7f; } - var headerLengthSize = (int)headerSize - 1; + var headerLengthSize = headerSize - 1; if(IsMasked) headerSize += 4; @@ -105,9 +105,9 @@ namespace Kneesocks { var lengthByte = raw[1] & 0x7F; return 2 - + ((raw[1] & 0x80) != 0 ? 4: 0) - + (lengthByte == 0x7E ? 2 : 0) - + (lengthByte == 0x7F ? 8 : 0); + + ((raw[1] & 0x80) != 0 ? 4 : 0) + + (lengthByte == 0x7E ? 2 : 0) + + (lengthByte == 0x7F ? 8 : 0); } public static Frame HeaderFromBytes(byte[] raw) { diff --git a/server/Libraries/Square/RandomContext.cs b/server/Libraries/Square/RandomContext.cs index 7015229..36b06bf 100644 --- a/server/Libraries/Square/RandomContext.cs +++ b/server/Libraries/Square/RandomContext.cs @@ -7,7 +7,7 @@ using System.Numerics; namespace Square { public static class RNG { - private static System.Random RandCtx = new System.Random(); + private static Random RandCtx = new Random(); public static int Next() { lock(RandCtx) { diff --git a/server/Socks/Packet.cs b/server/Socks/Packet.cs new file mode 100644 index 0000000..1ee8500 --- /dev/null +++ b/server/Socks/Packet.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Square; + +namespace CircleScape { + class Packet { + public enum kId { + KeyExchange = 0, + LoginAttempt, + RegistrationAttempt + } + + private static Packet ErrorPacket { + get { + return new Packet { IsLegal = false }; + } + } + + public static Packet FromRaw(byte[] raw) { + if(raw.Length < 3) + return ErrorPacket; + + Packet packet = new Packet(); + if(!Enum.IsDefined(typeof(kId), raw[0])) + return ErrorPacket; + packet.Id = (kId)raw[0]; + var regionCount = raw[1]; + var regionLengths = new List(); + var headerPtr = 2; + for(var i = 0; i < regionCount; ++i) { + var first = raw[headerPtr]; + if(first < 254) { + regionLengths[i] = first; + ++headerPtr; + } else if(first == 254) { + if(headerPtr + 3 < raw.Length) + return ErrorPacket; + regionLengths[i] = raw.Subset(headerPtr + 1, 2).UnpackUInt16(); + headerPtr += 3; + } else { + if(headerPtr + 5 < raw.Length) + return ErrorPacket; + regionLengths[i] = raw.Subset(headerPtr + 1, 4).UnpackUInt32(); + headerPtr += 5; + } + + if(headerPtr > raw.Length) + return ErrorPacket; + } + + if(headerPtr + regionLengths.Sum(x => x) < raw.Length) + return ErrorPacket; + + long bodyPtr = headerPtr; + foreach(var regionLength in regionLengths) { + // FLAG this could fail if one region exceeds 2^31-1 in size, check later + packet.Regions.Add(raw.Subset((int)bodyPtr, (int)regionLength)); + bodyPtr += regionLength; + } + + return packet; + } + + private List Regions = new List(); + public kId Id { get; private set; } = kId.KeyExchange; + public bool IsLegal { get; private set; } = true; + public int RegionCount { + get { + return Regions.Count; + } + } + + public Packet(kId id, params object[] regions) { + Id = id; + + foreach(var region in regions) { + if(region.GetType() == typeof(byte[])) + Regions.Add((byte[])region); + else if(region.GetType() == typeof(string)) + Regions.Add(((string)region).GetBytes()); + } + } + + public Region this[int i] { + get { + return new Region(Regions[i]); + } + } + + public byte[] GetBytes() { + if(!IsLegal) + return null; + + var header = new List(); + IEnumerable body = new byte[0]; + foreach(var region in Regions) { + if(region.Length < 254) + header.Add((byte)region.Length); + else if(region.Length <= 0xFFFF) { + header.Add(254); + header.AddRange(((UInt16)region.Length).Pack()); + } else { + header.Add(255); + header.AddRange(region.Length.Pack()); + } + + body = body.Concat(region); + } + + return header.Concat(body).ToArray(); + } + } + + class Region { + public byte[] Data { get; private set; } + + public Region(byte[] data) { + Data = data; + } + + public static implicit operator byte[](Region region) { + return region.Data; + } + + public static implicit operator string(Region region) { + try { + return Encoding.UTF8.GetString(region.Data); + } catch { + return Encoding.ASCII.GetString(region.Data); + } + } + } +}