diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 67dd9f8..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "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 deleted file mode 100644 index c2e2b15..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "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 8381c75..3751555 100644 --- a/client/Program.cs +++ b/client/Program.cs @@ -1,50 +1,2 @@ -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 +// See https://aka.ms/new-console-template for more information +Console.WriteLine("Hello, World!"); diff --git a/client/client.csproj b/client/client.csproj index 84912b2..47ba6e4 100644 --- a/client/client.csproj +++ b/client/client.csproj @@ -7,7 +7,7 @@ Exe net8.0 - disable + enable enable diff --git a/lib/Class1.cs b/lib/Class1.cs new file mode 100644 index 0000000..34def2d --- /dev/null +++ b/lib/Class1.cs @@ -0,0 +1,6 @@ +namespace lib; + +public class Class1 +{ + +} diff --git a/lib/Request.cs b/lib/Request.cs deleted file mode 100644 index e954151..0000000 --- a/lib/Request.cs +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index 8139470..0000000 --- a/lib/Utils.cs +++ /dev/null @@ -1,53 +0,0 @@ -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 6e29e6d..704057d 100644 --- a/online_security_project.sln +++ b/online_security_project.sln @@ -5,10 +5,6 @@ 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 @@ -22,13 +18,5 @@ 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 26b32d8..4bee316 100644 --- a/protocol.md +++ b/protocol.md @@ -3,29 +3,34 @@ 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 + +## Some problems -### Key Derivation Function +Do i need hasing? dont think it will help too much, can just sign-encrypt i think. -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 +When asking for messages, maybe do a little back and forth with the server to make sure +the request comes from the correct person? +Maybe keep track of the last message ID every time? then add it to the request, + not good, as if there were no new messages an attacker can now use said req. +Maybe the current timestamp? then the server keeps track of the last request with +timestamp for a given user? kinda acts like a better counter i think. + I dont think someone can abuse this system. + I think adding this to every request is neccessary to make sure no duplicates are + being sent, ie. if an attacker listens on the channel and sees a package sent to + the server, it can repeat it and cause mischief (maybe send duplicate messages, + or flush the user's messages or something). + + an attacker can also abuse duplicates to see who a user is talking to, + simply send the request and encrypt all public keys (gotten in a legitimate way) + using the user's public key (as the server would encrypt) and compare the result + from the server + ## 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 `VerificationRequired` message & a 6-digit code (Secure channel) +- Server sends the user a `Confirm` message & a 6-digit code - 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) @@ -46,40 +51,19 @@ 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 +## 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 +legend: + - `E(t)` encrypt t + - `S(t)` sign t + - `ES(t)` signed and encrypted (in that order!) + - `HS(t)`/`HS t` hash and sign t +All requests are to be encrypted using the server's public key, have the sender phone +and have a timestamp on them, except when otherwise noted -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 +1. `Register(public key)`, no timestamp +2. `HS RegisterConfirm(6-digit code)` +3. `GetMessages` +4. `GetUserKey(phone - user to get key for)` +5. `SendMessage(phone - receiver, ES({ message_id, message }))` +6. `SendMessageACK(phone - ack receiver, ES({ message_id[], ACK/NACK }))` diff --git a/server/Program.cs b/server/Program.cs index e361765..3751555 100644 --- a/server/Program.cs +++ b/server/Program.cs @@ -1,44 +1,2 @@ -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 +// See https://aka.ms/new-console-template for more information +Console.WriteLine("Hello, World!"); diff --git a/server/server.csproj b/server/server.csproj index 84912b2..47ba6e4 100644 --- a/server/server.csproj +++ b/server/server.csproj @@ -7,7 +7,7 @@ Exe net8.0 - disable + enable enable