started working o nstuff yay
This commit is contained in:
parent
2df8de0a3e
commit
65f5a95dac
11 changed files with 295 additions and 13 deletions
26
.vscode/launch.json
vendored
Normal file
26
.vscode/launch.json
vendored
Normal file
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
41
.vscode/tasks.json
vendored
Normal file
41
.vscode/tasks.json
vendored
Normal file
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,2 +1,50 @@
|
||||||
// See https://aka.ms/new-console-template for more information
|
using System;
|
||||||
Console.WriteLine("Hello, World!");
|
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}");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
namespace lib;
|
|
||||||
|
|
||||||
public class Class1
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
10
lib/Request.cs
Normal file
10
lib/Request.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace lib;
|
||||||
|
|
||||||
|
public enum RequestType {
|
||||||
|
Register = 1,
|
||||||
|
ConfirmRegister = 2,
|
||||||
|
GetMessages = 3,
|
||||||
|
GetUserKey = 4,
|
||||||
|
SendMessage = 5,
|
||||||
|
SendAck = 6
|
||||||
|
}
|
53
lib/Utils.cs
Normal file
53
lib/Utils.cs
Normal file
|
@ -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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,10 @@ VisualStudioVersion = 17.0.31903.59
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client", "client\client.csproj", "{F38E98E8-4FE8-4F34-B5A7-004444ED1F50}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client", "client\client.csproj", "{F38E98E8-4FE8-4F34-B5A7-004444ED1F50}"
|
||||||
EndProject
|
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
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{F38E98E8-4FE8-4F34-B5A7-004444ED1F50}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
58
protocol.md
58
protocol.md
|
@ -3,11 +3,29 @@
|
||||||
All encryptions are made using RSA (key size to be determined), no symmetric encryptions
|
All encryptions are made using RSA (key size to be determined), no symmetric encryptions
|
||||||
are used due to messages being short.
|
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:
|
## Registration:
|
||||||
|
|
||||||
- User sends a register request to server `(phone number, RSA public key)`,
|
- 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
|
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
|
- 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
|
(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)
|
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
|
The server will hold on to the message until B will send a `GetMessages` request
|
||||||
to the server.
|
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
|
|
@ -1,2 +1,44 @@
|
||||||
// See https://aka.ms/new-console-template for more information
|
using System;
|
||||||
Console.WriteLine("Hello, World!");
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue