diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..67dd9f8 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/client/bin/Debug/net8.0/client.dll", + "args": [], + "cwd": "${workspaceFolder}/client", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..c2e2b15 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/online_security_project.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/online_security_project.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/online_security_project.sln" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/client/Program.cs b/client/Program.cs index 3751555..8381c75 100644 --- a/client/Program.cs +++ b/client/Program.cs @@ -1,2 +1,50 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); +using System; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace Client; + +public class Program +{ + static void Main(string[] args) + { + using TcpClient client = new("127.0.0.1", 12345); + + byte[] toSend = Encoding.ASCII.GetBytes("hello server"); + + var stream = client.GetStream(); + + var inputTask = Task.Run(async () => await HandleUserInput(client, stream)); + var serverInput = Task.Run(async () => await HandleServerInput(client, stream)); + + _ = Task.WaitAny(inputTask, serverInput); + + } + + static async Task HandleUserInput(TcpClient client, NetworkStream stream) { + while(client.Connected) { + string? input = Console.ReadLine(); + if(input == null) { + await Task.Delay(100); + continue; + } + else { + stream.Write(Encoding.ASCII.GetBytes(input)); + Console.WriteLine($"[{DateTime.Now}]Sent to server: {input}"); + } + } + } + + static async Task HandleServerInput(TcpClient client, NetworkStream stream) { + byte[] buffer = new byte[1024]; + while(client.Connected) { + int readLen = await stream.ReadAsync(buffer); + if(readLen != 0) { + string fromServer = Encoding.ASCII.GetString(buffer[..readLen]); + Console.WriteLine($"[{DateTime.Now}]\t\tFrom server: {fromServer}"); + + } + } + } +} \ No newline at end of file diff --git a/client/client.csproj b/client/client.csproj index 47ba6e4..84912b2 100644 --- a/client/client.csproj +++ b/client/client.csproj @@ -7,7 +7,7 @@ Exe net8.0 - enable + disable enable diff --git a/lib/Class1.cs b/lib/Class1.cs deleted file mode 100644 index 34def2d..0000000 --- a/lib/Class1.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace lib; - -public class Class1 -{ - -} diff --git a/lib/Request.cs b/lib/Request.cs new file mode 100644 index 0000000..e954151 --- /dev/null +++ b/lib/Request.cs @@ -0,0 +1,10 @@ +namespace lib; + +public enum RequestType { + Register = 1, + ConfirmRegister = 2, + GetMessages = 3, + GetUserKey = 4, + SendMessage = 5, + SendAck = 6 +} \ No newline at end of file diff --git a/lib/Utils.cs b/lib/Utils.cs new file mode 100644 index 0000000..8139470 --- /dev/null +++ b/lib/Utils.cs @@ -0,0 +1,53 @@ +using System.Text; +using System.Linq; + +namespace lib; + +public static class Utils { + public static byte[] NumberToBytes(string Number) { + if(Number.Any(c => !char.IsDigit(c)) || Number.Length > 16) { + throw new Exception("Invalid arguments!"); + } + byte[] res = Enumerable.Repeat((byte)0b1111_1111, 8).ToArray(); + // Pad Number if needed to be of even length (because each 2 digits are turned into 1 byte) + Number = Number.Length % 2 == 0 ? Number : Number + '-'; + + for(int i = 0; i < Number.Length - 1; i += 2) { + char c1 = Number[i]; + char c2 = Number[i + 1]; + res[i / 2] = (byte)((DigitToByte(c1) << 4) | DigitToByte(c2)); + } + return res; + } + + public static string BytesToNumber(byte[] Bytes) { + string s = ""; + foreach(byte b in Bytes) { + byte b1 = (byte)((b >> 4) & 0b1111); + byte b2 = (byte)(b & 0b1111); + s = s + ByteToDigit(b1) + ByteToDigit(b2); + } + return new string(s.Where(c => char.IsDigit(c)).ToArray()); + } + + + public static byte DigitToByte(char c) { + if (int.TryParse(c.ToString(), out int d)) { + return (byte)d; + } + else { + return 0b1111; // empty, turned into '-' later to be discarded + } + } + + public static char ByteToDigit(byte b) { + byte offset = Encoding.ASCII.GetBytes("0")[0]; + + if(b == 0b1111) { + return '-'; + } + else { + return Encoding.ASCII.GetChars([(byte)(b + offset)])[0]; + } + } +} \ No newline at end of file diff --git a/online_security_project.sln b/online_security_project.sln index 704057d..6e29e6d 100644 --- a/online_security_project.sln +++ b/online_security_project.sln @@ -5,6 +5,10 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client", "client\client.csproj", "{F38E98E8-4FE8-4F34-B5A7-004444ED1F50}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server", "server\server.csproj", "{36C3F9FE-FA02-4AC0-89A2-65B377767C8F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "lib", "lib\lib.csproj", "{8E625330-2219-4E74-8524-A947D783B3BB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +22,13 @@ Global {F38E98E8-4FE8-4F34-B5A7-004444ED1F50}.Debug|Any CPU.Build.0 = Debug|Any CPU {F38E98E8-4FE8-4F34-B5A7-004444ED1F50}.Release|Any CPU.ActiveCfg = Release|Any CPU {F38E98E8-4FE8-4F34-B5A7-004444ED1F50}.Release|Any CPU.Build.0 = Release|Any CPU + {36C3F9FE-FA02-4AC0-89A2-65B377767C8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36C3F9FE-FA02-4AC0-89A2-65B377767C8F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36C3F9FE-FA02-4AC0-89A2-65B377767C8F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36C3F9FE-FA02-4AC0-89A2-65B377767C8F}.Release|Any CPU.Build.0 = Release|Any CPU + {8E625330-2219-4E74-8524-A947D783B3BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E625330-2219-4E74-8524-A947D783B3BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E625330-2219-4E74-8524-A947D783B3BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E625330-2219-4E74-8524-A947D783B3BB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/protocol.md b/protocol.md index f999141..26b32d8 100644 --- a/protocol.md +++ b/protocol.md @@ -3,11 +3,29 @@ All encryptions are made using RSA (key size to be determined), no symmetric encryptions are used due to messages being short. +a. Encryption will be a-symmetric because we can assume messages are short +b. See "Registration" below +c. Server keeps public keys for everyone, and gives them to a user when they want to +send a message to someone (only the relevant key) +d. Every e2e message is both signed and encrypted aka, message M from A to B +`EM = E_Bpub(E_Apriv(N))` +e. message is sent to the server in a `E_server(E_Apriv(metadata + EM from d))`, +message ack is sent to the server as a `MessageAck(msg_id)`, which will be retrieved +by the user when it pulls for messages. +f. TODO: properly explain the different api structs +g. TODO: properly explain how the server handles data + +### Key Derivation Function + +as there is no use of symmetric keys (at least for this scope), I dont believe there +is a need to use KDF, it will reduce the keyspace and we already have the public key +of the server in the clients + ## Registration: - User sends a register request to server `(phone number, RSA public key)`, giving them a public key and encrypting using the server's public key -- Server sends the user a `Confirm` message & a 6-digit code +- Server sends the user a `VerificationRequired` message & a 6-digit code (Secure channel) - User sends the server the 6-digit code, signed using the key provided at stage 1 (this message is very short and funny, because its a 6-digit code, signed using the user's private key, and encrypted using the server's public key) @@ -27,3 +45,41 @@ request and ultimately signed by A and encrypted using the server's key. The server will hold on to the message until B will send a `GetMessages` request to the server. + +## "Control" requests + +- Register `(phone, pub RSA key)` + extra data: RSA key size (payload length) +- ConfirmRegister (signed & encrypted 6 digit code) + extra data: 6 bytes for the 6 digit code (can use less but it will be padding otherwise) +- GetMessages (signed & encrypted to not allow someone to "flush" someone else's msgs) + extra data: EMPTY +- GetUserKey (dont think it needs any signing or encryption technically, as it is very + simple registering and "stealing" a key outside the system, keys are assumed to be + unbreakable) + extra data: 8 bytes (4 bits per digit) of whoever we want to get the key of +- SendMessage `(to_user, msg_id, EM from d above)` + extra data: 8 bytes (4 bits per digit) of who to send the data, 4 bytes (32bit) for length +- SendAck `(to_user, msg_id - signed and encrypted)` + - server doesnt know if the msg itself is indeed valid and not tempered with + extra data: 8 bytes (4 bits per digit) of who to send the data, 4 bytes (32bit) for length + +I think it all can go into a: +``` +{ + Version byte (0) - 1 byte + RequestType - 1 byte, + Phone number - 8 bytes (every 4 bits is a number, so we have 16 numbers), + UTC timestamp - 8 bytes (long), + extra data (based on the request given) - up to 18 bytes +} = 32 bytes = 256 bits +``` +and we can just append encrypted payloads to it (in SendMessage and SendAck) + +To each message we also append a sha3-256 signed hash + +enc_server( request - 256 bits, signed sha3-256 ) + +which means: + Keys: RSA-512 + Hashes: SHA3-256 \ No newline at end of file diff --git a/server/Program.cs b/server/Program.cs index 3751555..e361765 100644 --- a/server/Program.cs +++ b/server/Program.cs @@ -1,2 +1,44 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); +using System; +using System.Net; +using System.Net.Sockets; + +namespace Server; + +public class Program +{ + static void Main(string[] args) + { + int port = 12345; + TcpListener server = new(IPAddress.Parse("0.0.0.0"), port); + try { + server.Start(); + byte[] buffer = new byte[256]; + + while(true) { + // Currently, every time it gets a block, it will simply send it back but ToUpper + using TcpClient client = server.AcceptTcpClient(); + Console.WriteLine("Got a client!"); + + var stream = client.GetStream(); + int readLen; + while((readLen = stream.Read(buffer, 0, buffer.Length)) != 0) { + // for now, lets just read it as an ascii string + string input = System.Text.Encoding.ASCII.GetString(buffer, 0, readLen); + Console.WriteLine($"Got block: {input}"); + + byte[] ret = System.Text.Encoding.ASCII.GetBytes(input.ToUpper()); + stream.Write(ret, 0, ret.Length); + } + + + } + } + catch(Exception ex) { + Console.WriteLine($"Server error: {ex.Message}"); + Console.WriteLine("Trace: " + ex.StackTrace); + } + finally { + server.Stop(); + } + } +} \ No newline at end of file diff --git a/server/server.csproj b/server/server.csproj index 47ba6e4..84912b2 100644 --- a/server/server.csproj +++ b/server/server.csproj @@ -7,7 +7,7 @@ Exe net8.0 - enable + disable enable