done with printing stuff, saving is non trivial due to classes without constructors or something

This commit is contained in:
Rusty Striker 2025-01-06 20:27:54 +02:00
parent 2aa4a86e5f
commit 2a3be450be
Signed by: RustyStriker
GPG key ID: 87E4D691632DFF15
8 changed files with 277 additions and 109 deletions

View file

@ -1,6 +1,9 @@
# Project - TODO: # Project - TODO:
[ ] Do print stuff for EVERYTHING (with colors preferably) [ ] Do print stuff for EVERYTHING (with colors preferably)
[x] Client
[x] Server
[ ] rewrite the protocol.md [ ] rewrite the protocol.md
[ ] Make a video that shows how everything works yada yada
[ ] export everything and upload it [ ] export everything and upload it
[ ] get a nice drink to celebrate [ ] get a nice drink to celebrate

44
client/Log.cs Normal file
View file

@ -0,0 +1,44 @@
using System;
using lib;
namespace client;
/// Helper class to pretty print stuff with colors, client specific
public static class Log
{
public static void Encrypted(string message, byte[] payload)
{
Console.ForegroundColor = Utils.C_ENC;
Console.WriteLine($"{message}: {Convert.ToBase64String(payload)}");
Console.ResetColor();
}
public static void Decrypted(string message, byte[] payload)
{
Console.ForegroundColor = Utils.C_DEC;
Console.WriteLine($"{message}: {Convert.ToBase64String(payload)}");
Console.ResetColor();
}
public static void Decrypted(string message)
{
Console.ForegroundColor = Utils.C_DEC;
Console.WriteLine(message);
Console.ResetColor();
}
public static void System(string message)
{
Console.ForegroundColor = Utils.C_SYS;
Console.WriteLine(message);
Console.ResetColor();
}
public static void Message(string message)
{
Console.WriteLine(message);
}
}

View file

@ -4,6 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.Json.Serialization;
using lib; using lib;
namespace client; namespace client;
@ -14,6 +15,7 @@ public class Message
public string Sender { set; get; } public string Sender { set; get; }
public bool IsAck { set; get; } public bool IsAck { set; get; }
public string Content { set; get; } public string Content { set; get; }
[JsonIgnore]
public byte[] Signature { set; get; } public byte[] Signature { set; get; }
public Message(byte[] bytes) public Message(byte[] bytes)

View file

@ -8,6 +8,7 @@ using System.Net.Sockets;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks; using System.Threading.Tasks;
using client; using client;
using lib; using lib;
@ -18,10 +19,11 @@ public class Program
{ {
static string User = ""; static string User = "";
static readonly Random Rand = new((int)DateTime.Now.Ticks); static readonly Random Rand = new((int)DateTime.Now.Ticks);
static byte counter = (byte)Rand.Next(); static byte Counter = (byte)Rand.Next();
static async Task Main(string[] args) static async Task Main(string[] args)
{ {
// Load some info stuff
User = args User = args
.SkipWhile(a => !new[] { "-p", "--phone" }.Contains(a)) // search for the option .SkipWhile(a => !new[] { "-p", "--phone" }.Contains(a)) // search for the option
.Skip(1) // skip the `-u/--user` itself to get the value .Skip(1) // skip the `-u/--user` itself to get the value
@ -29,22 +31,32 @@ public class Program
// On boot, check if a key is available // On boot, check if a key is available
RSA? serverKey = LoadRSAFromFile("server_key.pem"); RSA? serverKey = LoadRSAFromFile("server_key.pem");
if (serverKey == null) { Console.WriteLine("Could not find server key, please run server before clients!"); return; } if (serverKey == null)
RSA pubKey = LoadRSAFromFile($"pubkey_{User}.pem") ?? RSA.Create(2048); {
Log.System("Could not find server key, please run server before clients!");
return;
}
// Attempt to load the user's keys,
RSA pubKey = LoadRSAFromFile($"pubkey_{User}.pem") ?? RSA.Create(1024);
RSA privKey = LoadRSAFromFile($"privkey_{User}.pem") ?? pubKey; RSA privKey = LoadRSAFromFile($"privkey_{User}.pem") ?? pubKey;
SaveRSAKeys(User, pubKey, privKey); SaveRSAKeys(User, pubKey, privKey);
using TcpClient client = new("127.0.0.1", 12345); // if pubKey and privKey is the same object then we created the keys just now, otherwise we can use the below flags to force a registration
bool needsRegister = pubKey == privKey || args.Any(a => new[] { "-fr", "--force-register" }.Contains(a));
// Connect to the server
using TcpClient client = new("127.0.0.1", 12345);
var stream = client.GetStream(); var stream = client.GetStream();
// First contact init // Establish secure connection
bool needsRegister = pubKey == privKey || args.Any(a => new[] { "-fr", "--force-register" }.Contains(a));
Aes sk = Aes.Create(); // creates an AES-256 key Aes sk = Aes.Create(); // creates an AES-256 key
// establish secure connection
Log.Decrypted($"Session key + IV", [.. sk.Key, .. sk.IV]);
byte[] skEnc = serverKey.Encrypt([.. sk.Key, .. sk.IV], RSAEncryptionPadding.OaepSHA256); byte[] skEnc = serverKey.Encrypt([.. sk.Key, .. sk.IV], RSAEncryptionPadding.OaepSHA256);
Log.Encrypted("Session key + IV", skEnc);
await stream.WriteAsync(skEnc); await stream.WriteAsync(skEnc);
// wait for the server to confirm it recieved the keys // wait for the server to confirm it recieved the keys, supposedly we would want to read exactly what we need then
// send as much as we can in 1 go (and maybe use buffered streams), but i didnt do that and im fine with that
await stream.ReadExactlyAsync(new byte[1]); await stream.ReadExactlyAsync(new byte[1]);
if (needsRegister) if (needsRegister)
@ -55,38 +67,48 @@ public class Program
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("Failed registration process"); Log.System("Failed registration process");
Console.WriteLine("Exception: " + ex.Message); Log.System("Exception: " + ex.Message);
Console.WriteLine("Stack: " + ex.StackTrace); Log.System("Stack: " + ex.StackTrace);
return; return;
} }
} }
else else
{ {
// start login process // start login process
byte[] msg = Request.CreateRequest(RequestType.Login, ref counter, Utils.NumberToBytes(User)); byte[] msg = Request.CreateRequest(RequestType.Login, ref Counter, Utils.NumberToBytes(User));
Log.Decrypted("Login", msg);
msg = sk.EncryptCfb(msg, sk.IV, PaddingMode.PKCS7); msg = sk.EncryptCfb(msg, sk.IV, PaddingMode.PKCS7);
Log.Encrypted("Login", msg);
await stream.WriteAsync(msg); await stream.WriteAsync(msg);
byte[] toSign = new byte[16]; byte[] toSign = new byte[16];
await stream.ReadExactlyAsync(toSign, 0, 16); await stream.ReadExactlyAsync(toSign, 0, 16);
Log.Encrypted("Challenge", toSign);
toSign = sk.DecryptCfb(toSign, sk.IV, PaddingMode.None); toSign = sk.DecryptCfb(toSign, sk.IV, PaddingMode.None);
Log.Decrypted("Challenge", toSign);
byte[] signed = privKey.SignData(toSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); byte[] signed = privKey.SignData(toSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
msg = Request.CreateRequest(RequestType.ConfirmLogin, ref counter, BitConverter.GetBytes(signed.Length)); Log.Decrypted("Signed", signed);
msg = Request.CreateRequest(RequestType.ConfirmLogin, ref Counter, BitConverter.GetBytes(signed.Length));
Log.Decrypted("Challenge message", msg);
msg = sk.EncryptCfb(msg, sk.IV, PaddingMode.None); msg = sk.EncryptCfb(msg, sk.IV, PaddingMode.None);
WriteColor($"Sending sig: {Convert.ToBase64String(signed)}", ConsoleColor.Green); Log.Encrypted("Challenge message", msg);
msg = [.. msg, .. signed]; msg = [.. msg, .. signed];
Log.Encrypted("Challenge final", msg);
await stream.WriteAsync(msg); await stream.WriteAsync(msg);
byte[] buffer = new byte[512]; byte[] buffer = new byte[512];
int len = await stream.ReadAsync(buffer); int len = await stream.ReadAsync(buffer);
Log.Encrypted("Result", buffer[..len]);
byte[] dec = sk.DecryptCfb(buffer[..len], sk.IV, PaddingMode.PKCS7); byte[] dec = sk.DecryptCfb(buffer[..len], sk.IV, PaddingMode.PKCS7);
Log.Decrypted("Result", dec);
string r = Encoding.UTF8.GetString(dec); string r = Encoding.UTF8.GetString(dec);
Log.Decrypted($"Result (UTF8): {JsonSerializer.Serialize(r.ToCharArray())}");
if (r == "OK") if (r == "OK")
{ {
Console.WriteLine("Login successful"); Log.System("Login successful");
} }
else else
{ {
Console.WriteLine($"Failed login: {r}"); Log.System($"Failed login: {r}");
client.Dispose(); client.Dispose();
return; return;
} }
@ -98,34 +120,29 @@ public class Program
async static Task RegisterClient(string user, RSA pub, RSA priv, Aes sk, NetworkStream stream) async static Task RegisterClient(string user, RSA pub, RSA priv, Aes sk, NetworkStream stream)
{ {
Console.WriteLine("Attempting to register with public key:"); Log.System("Attempting to register with public key:");
Console.WriteLine(pub.ExportRSAPublicKeyPem()); Log.System(pub.ExportRSAPublicKeyPem());
// Generate aes key and send it forward
Console.WriteLine($"Session key: {string.Join(' ', sk.Key)}");
Console.WriteLine($"Session IV: {string.Join(' ', sk.IV)}");
// Generate the Register msg // Generate the Register msg
Console.WriteLine("Sending rsa public key thing");
byte[] pubBytes = pub.ExportRSAPublicKey(); byte[] pubBytes = pub.ExportRSAPublicKey();
byte[] data = new byte[12]; byte[] data = new byte[12];
Array.Copy(Utils.NumberToBytes(user), data, 8); Array.Copy(Utils.NumberToBytes(user), data, 8);
Array.Copy(BitConverter.GetBytes(pubBytes.Length), 0, data, 8, 4); Array.Copy(BitConverter.GetBytes(pubBytes.Length), 0, data, 8, 4);
byte[] msg = Request.CreateRequest(RequestType.Register, ref counter, data); byte[] msg = Request.CreateRequest(RequestType.Register, ref Counter, data);
// Encrypt msg and send it // Encrypt msg and send it
byte[] payload = [.. msg, .. pubBytes]; byte[] payload = [.. msg, .. pubBytes];
Log.Decrypted("Register payload", payload);
byte[] enc = sk.EncryptCfb(payload, sk.IV, PaddingMode.PKCS7); byte[] enc = sk.EncryptCfb(payload, sk.IV, PaddingMode.PKCS7);
Console.WriteLine($"payload length: {enc.Length}"); Log.Encrypted("Register payload", enc);
await stream.WriteAsync(enc); await stream.WriteAsync(enc);
// get the 6 digit code (from "secure channel", actually an OK message is expected here but the 6 digit code kinda replaces it) // get the 6 digit code (from "secure channel", actually an OK message is expected here but the 6 digit code kinda replaces it)
byte[] digits = new byte[6]; byte[] digits = new byte[6];
int len = 0; int len = await stream.ReadAsync(digits);
while (len != 6) Log.System($"6 digit code actual length: {len}");
{
len = await stream.ReadAsync(digits);
}
// print the 6 digit code // print the 6 digit code
Console.WriteLine($"[{DateTime.Now}] 6 digit code: {string.Join(' ', digits.Select(d => d.ToString()))}"); Log.System($"[{DateTime.Now}] 6 digit code: {string.Join(' ', digits.Select(d => d.ToString()))}");
// get the 6 digit code from the user // get the 6 digit code from the user
while (true) while (true)
{ {
@ -133,7 +150,8 @@ public class Program
string? code = Console.ReadLine()?.Trim(); string? code = Console.ReadLine()?.Trim();
if (code == null || code.Take(6).Any(c => !char.IsDigit(c))) if (code == null || code.Take(6).Any(c => !char.IsDigit(c)))
{ {
Console.WriteLine("Invalid code!"); // we know the code is invalid because it HAS to be a 6 digit code
Log.System("Invalid code!");
continue; continue;
} }
byte[] codeBytes = code byte[] codeBytes = code
@ -141,18 +159,25 @@ public class Program
.Select(d => byte.Parse(d.ToString())) // parse into bytes .Select(d => byte.Parse(d.ToString())) // parse into bytes
.ToArray(); .ToArray();
// Debug print the inserted value to see it works :) // Debug print the inserted value to see it works :)
Console.WriteLine(string.Join(' ', codeBytes.Select(b => b.ToString()))); Log.System(string.Join(' ', codeBytes.Select(b => b.ToString())));
// Sign the 6 digit code & Generate ConfirmRegister message // Sign the 6 digit code & Generate ConfirmRegister message
byte[] signed = priv.SignData(codeBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); byte[] signed = priv.SignData(codeBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
msg = Request.CreateRequest(RequestType.ConfirmRegister, ref counter, [.. codeBytes, .. BitConverter.GetBytes(signed.Length)]); Log.Decrypted("Signed code", signed);
msg = Request.CreateRequest(RequestType.ConfirmRegister, ref Counter, [.. codeBytes, .. BitConverter.GetBytes(signed.Length)]);
Log.Decrypted("Signed message", msg);
enc = sk.EncryptCfb(msg, sk.IV, PaddingMode.None); // no reason to encrpy the signature enc = sk.EncryptCfb(msg, sk.IV, PaddingMode.None); // no reason to encrpy the signature
Log.Encrypted("Signed message", enc);
payload = [.. enc, .. signed]; // should be 128 (enc) + 256 (signed) payload = [.. enc, .. signed]; // should be 128 (enc) + 256 (signed)
Log.Encrypted("Signed final", payload);
await stream.WriteAsync(payload); await stream.WriteAsync(payload);
// wait for OK/NACK response (anything other than OK is a NACK) // wait for OK/NACK response (anything other than OK is a NACK)
int incoming = await stream.ReadAsync(enc); int incoming = await stream.ReadAsync(enc);
Log.Encrypted("Response", enc[..incoming]);
msg = sk.DecryptCfb(enc[..incoming], sk.IV, PaddingMode.PKCS7); msg = sk.DecryptCfb(enc[..incoming], sk.IV, PaddingMode.PKCS7);
Log.Decrypted("Response", msg);
string r = Encoding.UTF8.GetString(msg); string r = Encoding.UTF8.GetString(msg);
Log.Decrypted($"Response (UTF8): {r}");
if (r == "OK") if (r == "OK")
{ {
Console.WriteLine("Registration process complete"); Console.WriteLine("Registration process complete");
@ -189,7 +214,7 @@ public class Program
Dictionary<string, RSA> publicKeys = []; Dictionary<string, RSA> publicKeys = [];
while (client.Connected) while (client.Connected)
{ {
Console.Write("> "); Console.Write((currentChat != null ? $"[{currentChat}] " : "") + "> ");
string? input = Console.ReadLine(); string? input = Console.ReadLine();
if (input == null) if (input == null)
{ {
@ -212,7 +237,7 @@ public class Program
currentChat = words.Length > 1 ? words[1] : null; currentChat = words.Length > 1 ? words[1] : null;
if (currentChat != null && currentChat.Length > 16) if (currentChat != null && currentChat.Length > 16)
{ {
Console.WriteLine("Invalid number: too long"); Log.System("Invalid number: too long");
currentChat = old; currentChat = old;
continue; continue;
} }
@ -228,7 +253,7 @@ public class Program
else else
{ {
currentChat = old; currentChat = old;
Console.WriteLine($"Reverting to previous chat: {currentChat ?? "none"}"); Log.System($"Reverting to previous chat: {currentChat ?? "none"}");
} }
} }
break; break;
@ -244,25 +269,30 @@ public class Program
{ {
if (currentChat == null) if (currentChat == null)
{ {
Console.WriteLine("No chat is active, please select chat using '/msg [number]' or '/chat [number]'"); Log.System("No chat is active, please select chat using '/msg [number]' or '/chat [number]'");
} }
else if (publicKeys.TryGetValue(currentChat!, out RSA? key)) else if (publicKeys.TryGetValue(currentChat!, out RSA? key))
{ {
// Pick the id as a random uint, good enough for the sake of testing // Pick the id as a random uint, good enough for the sake of testing
Message m = new((uint)Rand.Next(), User, false, input); Message m = new((uint)Rand.Next(), User, false, input);
m.CalculateSignature(privKey); m.CalculateSignature(privKey);
Log.Decrypted($"Message: {JsonSerializer.Serialize(m)}");
Log.Decrypted("Message sig", m.Signature);
byte[] userMsg = [.. key.Encrypt(m.Bytes(), RSAEncryptionPadding.OaepSHA256), .. m.Signature]; byte[] userMsg = [.. key.Encrypt(m.Bytes(), RSAEncryptionPadding.OaepSHA256), .. m.Signature];
Log.Encrypted("Message", userMsg);
byte[] req = Request.CreateRequest( byte[] req = Request.CreateRequest(
RequestType.SendMessage, RequestType.SendMessage,
ref counter, ref Counter,
[.. Utils.NumberToBytes(currentChat!), .. BitConverter.GetBytes(userMsg.Length)]); [.. Utils.NumberToBytes(currentChat!), .. BitConverter.GetBytes(userMsg.Length)]);
Log.Decrypted("Message req", req);
req = sk.EncryptCfb(req, sk.IV); req = sk.EncryptCfb(req, sk.IV);
Log.Encrypted("Message req", req);
stream.Write([.. req, .. userMsg]); stream.Write([.. req, .. userMsg]);
Console.WriteLine($"[{DateTime.Now}] Sent to server: {input}"); Log.System($"[{DateTime.Now}] Sent to server: {input}");
} }
else else
{ {
Console.WriteLine($"active chat exists, but no key was found..."); Log.System($"active chat exists, but no key was found...");
} }
} }
} }
@ -270,45 +300,50 @@ public class Program
static async Task<RSA?> GetPublicKey(NetworkStream stream, Aes sk, string chat) static async Task<RSA?> GetPublicKey(NetworkStream stream, Aes sk, string chat)
{ {
byte[] req = Request.CreateRequest(RequestType.GetUserKey, ref counter, Utils.NumberToBytes(chat)); byte[] req = Request.CreateRequest(RequestType.GetUserKey, ref Counter, Utils.NumberToBytes(chat));
Log.Decrypted("Get key req", req);
req = sk.EncryptCfb(req, sk.IV, PaddingMode.None); // no need for padding this is exactly 128 bytes req = sk.EncryptCfb(req, sk.IV, PaddingMode.None); // no need for padding this is exactly 128 bytes
Log.Encrypted("Get key req", req);
await stream.WriteAsync(req); await stream.WriteAsync(req);
byte[] response = new byte[1024]; byte[] response = new byte[1024];
int len = await stream.ReadAsync(response); int len = await stream.ReadAsync(response);
Log.Encrypted("Key", response[..len]);
byte[] key = sk.DecryptCfb(response[..len], sk.IV, PaddingMode.PKCS7); byte[] key = sk.DecryptCfb(response[..len], sk.IV, PaddingMode.PKCS7);
Log.Decrypted("Key", key);
if (key[0] == 1) if (key[0] == 1)
{ {
Console.WriteLine($"failed getting key for {chat}: {Encoding.UTF8.GetString(key[1..])}"); Log.System($"failed getting key for {chat}: {Encoding.UTF8.GetString(key[1..])}");
return null; return null;
} }
else else
{ {
RSA bobsKey = RSA.Create(); RSA bobsKey = RSA.Create();
bobsKey.ImportRSAPublicKey(key.AsSpan()[1..], out int _); bobsKey.ImportRSAPublicKey(key.AsSpan()[1..], out int _);
Console.WriteLine($"Got key:\n {bobsKey.ExportRSAPublicKeyPem()}\n"); Log.System($"Got key:\n {bobsKey.ExportRSAPublicKeyPem()}\n");
return bobsKey; return bobsKey;
} }
} }
static async Task GetMessages(NetworkStream stream, Aes sk, RSA privKey, Dictionary<string, RSA> publicKeys) static async Task GetMessages(NetworkStream stream, Aes sk, RSA privKey, Dictionary<string, RSA> publicKeys)
{ {
byte[] req = Request.CreateRequest(RequestType.GetMessages, ref counter, []); byte[] req = Request.CreateRequest(RequestType.GetMessages, ref Counter, []);
Log.Decrypted("Get messages", req);
req = sk.EncryptCfb(req, sk.IV, PaddingMode.None); // no need for padding this is exactly 128 bytes req = sk.EncryptCfb(req, sk.IV, PaddingMode.None); // no need for padding this is exactly 128 bytes
Log.Encrypted("Get messages", req);
await stream.WriteAsync(req); await stream.WriteAsync(req);
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
int len = await stream.ReadAsync(buffer); int len = await stream.ReadAsync(buffer);
Log.Encrypted("Response", buffer[..len]);
byte[] lengths = buffer[..16]; byte[] lengths = buffer[..16];
lengths = sk.DecryptCfb(lengths, sk.IV, PaddingMode.None); lengths = sk.DecryptCfb(lengths, sk.IV, PaddingMode.None);
Console.ForegroundColor = ConsoleColor.Cyan; Log.Decrypted($"Lengths: {string.Join(", ", lengths)}");
Console.WriteLine($"Got messages: {string.Join(", ", lengths)}");
Console.ResetColor();
byte[] msg = new byte[1024]; // msg buffer byte[] msg = new byte[1024]; // msg buffer
int start = 16; // skip the first 16 bytes since its the lengths message int start = 16; // skip the first 16 bytes since its the lengths message
for (int i = 0; i < lengths.Length - 1; i += 2) for (int i = 0; i < lengths.Length - 1; i += 2)
{ {
int l = (lengths[i] << 8) | (lengths[i + 1]); int l = (lengths[i] << 8) | (lengths[i + 1]);
Console.WriteLine($"got message of length: {l}");
if (l == 0) { break; } // a 0 means we are done actually, as empty messages shouldn't be allowed if (l == 0) { break; } // a 0 means we are done actually, as empty messages shouldn't be allowed
// get the msg // get the msg
int end = start + l; int end = start + l;
@ -317,17 +352,19 @@ public class Program
// TODO: properly handle it as a buffered stream and properly buffer it // TODO: properly handle it as a buffered stream and properly buffer it
} }
WriteColor($"got ecnryped message: {Convert.ToBase64String(buffer[start..end])}", ConsoleColor.Green); Log.Encrypted($"Message {i / 2}", buffer[start..end]);
// decrypt the message // decrypt the message
int msgLen = privKey.KeySize / 8; int msgLen = privKey.KeySize / 8; // message length is derived from the size of the key
if (privKey.TryDecrypt(buffer.AsSpan()[start..(msgLen + start)], msg, RSAEncryptionPadding.OaepSHA256, out int written)) if (privKey.TryDecrypt(buffer.AsSpan()[start..(msgLen + start)], msg, RSAEncryptionPadding.OaepSHA256, out int written))
{ {
byte[] dec = msg[..written]; byte[] dec = msg[..written];
Log.Decrypted($"Message {i / 2}", dec);
Message m = new(dec) Message m = new(dec)
{ {
Signature = buffer[(start + msgLen)..end] Signature = buffer[(start + msgLen)..end]
}; };
Log.Decrypted($"Message {i / 2}: {JsonSerializer.Serialize(m)}");
Log.Decrypted($"Message {i / 2} sig", m.Signature);
bool sigValid = false; bool sigValid = false;
if (publicKeys.TryGetValue(m.Sender, out RSA? pk) || (pk = await GetPublicKey(stream, sk, m.Sender)) != null) if (publicKeys.TryGetValue(m.Sender, out RSA? pk) || (pk = await GetPublicKey(stream, sk, m.Sender)) != null)
{ {
@ -336,21 +373,25 @@ public class Program
} }
else else
{ {
Console.WriteLine("Failed getting sender's public key"); Log.System("Failed getting sender's public key - cannot verify signature");
} }
WriteColor($"decrypted message: {Convert.ToBase64String(dec)}", ConsoleColor.Green); Log.Message($"Got message from {m.Sender} (id: {m.Id}) (valid: {sigValid}): " + (m.IsAck ? "ACK" : m.Content));
Console.WriteLine($"Got message from {m.Sender} (id: {m.Id}) (valid: {sigValid}): " + (m.IsAck ? "ACK" : m.Content));
// if the signature is valid we also want to send back a message ack for the same id :) // if the signature is valid we also want to send back a message ack for the same id :)
if (sigValid) if (sigValid && !m.IsAck)
{ {
Message mAck = new(m.Id, User, true, ""); Message mAck = new(m.Id, User, true, "");
Log.Decrypted($"Ack {i / 2}: {JsonSerializer.Serialize(mAck)}");
mAck.CalculateSignature(privKey); mAck.CalculateSignature(privKey);
Log.Decrypted($"Ack {i / 2} sig", mAck.Signature);
byte[] userMsg = [.. publicKeys[m.Sender].Encrypt(mAck.Bytes(), RSAEncryptionPadding.OaepSHA256), .. mAck.Signature]; byte[] userMsg = [.. publicKeys[m.Sender].Encrypt(mAck.Bytes(), RSAEncryptionPadding.OaepSHA256), .. mAck.Signature];
Log.Encrypted($"Ack {i / 2}", userMsg);
byte[] reqAck = Request.CreateRequest( byte[] reqAck = Request.CreateRequest(
RequestType.SendMessage, RequestType.SendMessage,
ref counter, ref Counter,
[.. Utils.NumberToBytes(m.Sender), .. BitConverter.GetBytes(userMsg.Length)]); [.. Utils.NumberToBytes(m.Sender), .. BitConverter.GetBytes(userMsg.Length)]);
Log.Decrypted($"Ack {i / 2} request", reqAck);
reqAck = sk.EncryptCfb(reqAck, sk.IV); reqAck = sk.EncryptCfb(reqAck, sk.IV);
Log.Encrypted($"Ack {i / 2} request", reqAck);
stream.Write([.. reqAck, .. userMsg]); stream.Write([.. reqAck, .. userMsg]);
} }
} }
@ -361,21 +402,13 @@ public class Program
// and i dont want the server to be aware of that, i think it makes more sense for the server to act as a relay // and i dont want the server to be aware of that, i think it makes more sense for the server to act as a relay
// and a buffer than an actual participant, so if the message is failing to decrypt that will go unnoticed. // and a buffer than an actual participant, so if the message is failing to decrypt that will go unnoticed.
// supposedly the sender will notice the lack of response and send it again // supposedly the sender will notice the lack of response and send it again
Console.WriteLine("Incoming message failed to decrypt, unknown sender"); Log.System("Incoming message failed to decrypt, unknown sender");
} }
start = end; start = end;
} }
bool hasMoreMessages = lengths[15] != 0; bool hasMoreMessages = lengths[15] != 0;
Console.WriteLine($"hasMoreMessages: {hasMoreMessages}"); Log.System($"hasMoreMessages: {hasMoreMessages}");
}
static void WriteColor(string Line, ConsoleColor Color, ConsoleColor Background = ConsoleColor.Black)
{
Console.ForegroundColor = Color;
Console.BackgroundColor = Background;
Console.WriteLine(Line);
Console.ResetColor();
} }
} }

View file

@ -1,10 +1,17 @@
using System.Linq;
using System.Text; using System.Text;
namespace lib; namespace lib;
public static class Utils public static class Utils
{ {
// some print color coding constants for ease of change later
/// Encrypted stuff
public const ConsoleColor C_ENC = ConsoleColor.Green;
/// Non encrypted stuff (either before encryption or after decryption)
public const ConsoleColor C_DEC = ConsoleColor.Red;
/// System messages
public const ConsoleColor C_SYS = ConsoleColor.Yellow;
public static byte[] NumberToBytes(string Number) public static byte[] NumberToBytes(string Number)
{ {
if (Number.Any(c => !char.IsDigit(c)) || Number.Length > 16) if (Number.Any(c => !char.IsDigit(c)) || Number.Length > 16)

47
server/Log.cs Normal file
View file

@ -0,0 +1,47 @@
using System;
using lib;
namespace server;
/// Printing and helper class for logging stuff on the server, this is not static so we dont have to keep on adding the id and client at every call
public class Log(int id, string? client)
{
public int Id { set; get; } = id;
public string? Client { set; get; } = client;
public void Encrypted(string message, byte[] payload)
{
Console.ForegroundColor = Utils.C_ENC;
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {Id} [{Client ?? "unknown"}] - {message}: {Convert.ToBase64String(payload)}");
Console.ResetColor();
}
public void Decrypted(string message, byte[] payload)
{
Console.ForegroundColor = Utils.C_DEC;
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {Id} [{Client ?? "unknown"}] - {message}: {Convert.ToBase64String(payload)}");
Console.ResetColor();
}
public void Decrypted(string message)
{
Console.ForegroundColor = Utils.C_DEC;
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {Id} [{Client ?? "unknown"}] - {message}");
Console.ResetColor();
}
public void System(string message)
{
Console.ForegroundColor = Utils.C_SYS;
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {Id} [{Client ?? "unknown"}] - {message}");
Console.ResetColor();
}
public void Message(string message)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {Id} [{Client ?? "unknown"}] - {message}");
}
}

View file

@ -7,6 +7,7 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Threading.Tasks; using System.Threading.Tasks;
using lib; using lib;
@ -23,7 +24,7 @@ public class Program
static async Task Main() static async Task Main()
{ {
// Generally this key would be static but since its not production yet we can generate it every time to make sure // Generally this key would be static but since its not production we can generate it every time to make sure
// the users has the key and could load it from file // the users has the key and could load it from file
RSA key = RSA.Create(1024); RSA key = RSA.Create(1024);
File.WriteAllText("server_key.pem", key.ExportRSAPublicKeyPem()); File.WriteAllText("server_key.pem", key.ExportRSAPublicKeyPem());
@ -31,6 +32,14 @@ public class Program
int port = 12345; int port = 12345;
TcpListener server = new(IPAddress.Parse("0.0.0.0"), port); TcpListener server = new(IPAddress.Parse("0.0.0.0"), port);
int connectionCounter = 0; int connectionCounter = 0;
Log log = new(-1, null);
Console.CancelKeyPress += delegate
{
Console.WriteLine("\nEXITING!");
server.Stop();
};
try try
{ {
server.Start(); server.Start();
@ -48,8 +57,8 @@ public class Program
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Client crashed: {ex.Message}"); log.System($"Client crashed: {ex.Message}");
Console.WriteLine(ex.StackTrace); log.System(ex.StackTrace ?? "MISSING STACK TRACE");
} }
}); });
connectionCounter += 1; connectionCounter += 1;
@ -57,8 +66,8 @@ public class Program
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Server error: {ex.Message}"); log.System($"Server error: {ex.Message}");
Console.WriteLine("Trace: " + ex.StackTrace); log.System("Trace: " + ex.StackTrace);
} }
finally finally
{ {
@ -68,31 +77,32 @@ public class Program
static async Task HandleClient(TcpClient client, int id, RSA pubKey) static async Task HandleClient(TcpClient client, int id, RSA pubKey)
{ {
Write(id, "Got a new client"); Log log = new(id, null);
log.System("Got a new client");
string clientPhone = ""; string clientPhone = "";
NetworkStream stream = client.GetStream(); NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
byte counter = 0; byte counter = 0;
// Get AES session key // Get AES session key
int len = await stream.ReadAsync(buffer); int len = await stream.ReadAsync(buffer);
Write(id, $"Got {len} bytes"); log.Encrypted("Key + IV", buffer[..len]);
byte[] skBytes = pubKey.Decrypt(buffer[..len], RSAEncryptionPadding.OaepSHA256); byte[] skBytes = pubKey.Decrypt(buffer[..len], RSAEncryptionPadding.OaepSHA256);
log.Decrypted("Key + IV", skBytes);
Aes sk = Aes.Create(); Aes sk = Aes.Create();
sk.Key = skBytes[..32]; // just to make sure no one sends a too big to be true key sk.Key = skBytes[..32]; // just to make sure no one sends a too big to be true key
sk.IV = skBytes[32..]; sk.IV = skBytes[32..];
Write(id, $"key: {string.Join(' ', sk.Key)}");
Write(id, $"IV: {string.Join(' ', sk.IV)}");
await stream.WriteAsync(new byte[] { 0 }); await stream.WriteAsync(new byte[] { 0 });
// Get first message (should be either login or register) // Get first message (should be either login or register)
len = await stream.ReadAsync(buffer); len = await stream.ReadAsync(buffer);
Write(id, $"Got {len} bytes"); log.Encrypted("Auth message", buffer[..len]);
byte[] msgDec = sk.DecryptCfb(buffer[..len], sk.IV, PaddingMode.PKCS7); byte[] msgDec = sk.DecryptCfb(buffer[..len], sk.IV, PaddingMode.PKCS7);
log.Decrypted("Auth message", msgDec);
byte[] msg = msgDec[..MSG_LEN]; byte[] msg = msgDec[..MSG_LEN];
Write(id, Request.RequestToString(msg)); log.Message(Request.RequestToString(msg));
if (msg[0] != 0) if (msg[0] != 0)
{ {
Write(id, "Invalid session id!"); log.System("Invalid message version!");
client.Dispose(); client.Dispose();
return; return;
} }
@ -102,13 +112,13 @@ public class Program
// Do register stuff // Do register stuff
// get phone number // get phone number
string phone = Utils.BytesToNumber(msg[3..11]); string phone = Utils.BytesToNumber(msg[3..11]);
Write(id, $"Client wants to register as {phone}"); log.System($"Client wants to register as {phone}");
clientPhone = phone; clientPhone = phone;
log.Client = phone;
int keyLen = BitConverter.ToInt32(msg, 11); int keyLen = BitConverter.ToInt32(msg, 11);
RSA pub = RSA.Create(); RSA pub = RSA.Create();
pub.ImportRSAPublicKey(msgDec.AsSpan()[MSG_LEN..], out int bytesRead); pub.ImportRSAPublicKey(msgDec.AsSpan()[MSG_LEN..], out int bytesRead);
Write(id, $"Imported key len: {bytesRead} while client claims it is {keyLen}"); log.System($"Imported key is: \n {pub.ExportRSAPublicKeyPem()}\n");
Write(id, $"Imported key is: \n {pub.ExportRSAPublicKeyPem()}\n");
// generate the 6 digit code and send it // generate the 6 digit code and send it
byte[] code = [ byte[] code = [
(byte)Rand.Next(10), (byte)Rand.Next(10),
@ -118,17 +128,19 @@ public class Program
(byte)Rand.Next(10), (byte)Rand.Next(10),
(byte)Rand.Next(10), (byte)Rand.Next(10),
]; ];
await Send6DigitCodeInSecureChannel(stream, code); await SendBySecureChannel(stream, code);
// wait for the code to be back with a key // wait for the code to be back with a key
int tries = 5; // allow 5 tries before closing the connection and forcing a restart int tries = 5; // allow 5 tries before closing the connection and forcing a restart
while (tries > 0) while (tries > 0)
{ {
tries -= 1; tries -= 1;
len = await stream.ReadAsync(buffer); len = await stream.ReadAsync(buffer);
Write(id, $"Got 6 digit code with sig, len: {len}"); log.Encrypted("ConfirmRegister", buffer[..len]);
msg = sk.DecryptCfb(buffer[..MSG_LEN], sk.IV, PaddingMode.None); msg = sk.DecryptCfb(buffer[..MSG_LEN], sk.IV, PaddingMode.None);
Write(id, Request.RequestToString(msg)); log.Decrypted("ConfirmRegister", msg);
log.Message(Request.RequestToString(msg));
byte[] sig = buffer[MSG_LEN..len]; byte[] sig = buffer[MSG_LEN..len];
log.Decrypted("ConfirmRegister (sig)", sig);
if (msg[0] != 0 || msg[1] != (byte)RequestType.ConfirmRegister || msg[2] != counter) if (msg[0] != 0 || msg[1] != (byte)RequestType.ConfirmRegister || msg[2] != counter)
{ {
// invalid or unexpected req, someone might be sending dups // invalid or unexpected req, someone might be sending dups
@ -139,13 +151,16 @@ public class Program
int expectedSigLen = BitConverter.ToInt32(msg, 9); int expectedSigLen = BitConverter.ToInt32(msg, 9);
if (expectedSigLen != len - MSG_LEN) if (expectedSigLen != len - MSG_LEN)
{ {
Write(id, $"expected sig len doesnt match read len: {expectedSigLen} / {len - MSG_LEN}"); log.System($"expected sig len doesnt match read len: {expectedSigLen} / {len - MSG_LEN}");
} }
// check if the codes are equal // check if the codes are equal
if (code.Zip(gottenCode).Any(a => a.First != a.Second)) if (code.Zip(gottenCode).Any(a => a.First != a.Second))
{ {
// codes are not equal, send a nack // codes are not equal, send a nack
// perhaps we should only send a SIG INVALID (or just FAILED) and hide the reason in case someone tries to guess the code
msg = sk.EncryptCfb(Encoding.UTF8.GetBytes("BAD CODE"), sk.IV, PaddingMode.PKCS7); msg = sk.EncryptCfb(Encoding.UTF8.GetBytes("BAD CODE"), sk.IV, PaddingMode.PKCS7);
log.Decrypted("Fail Register", Encoding.UTF8.GetBytes("BAD CODE"));
log.Encrypted("Fail Register", msg);
await stream.WriteAsync(msg); await stream.WriteAsync(msg);
} }
else else
@ -155,6 +170,8 @@ public class Program
if (sigValid) if (sigValid)
{ {
msg = sk.EncryptCfb(Encoding.UTF8.GetBytes("OK"), sk.IV, PaddingMode.PKCS7); msg = sk.EncryptCfb(Encoding.UTF8.GetBytes("OK"), sk.IV, PaddingMode.PKCS7);
log.Decrypted("Register Success", Encoding.UTF8.GetBytes("OK"));
log.Encrypted("Register Success", msg);
await stream.WriteAsync(msg); await stream.WriteAsync(msg);
Data.Keys[phone] = pub; // save the key Data.Keys[phone] = pub; // save the key
break; break;
@ -162,6 +179,8 @@ public class Program
else else
{ {
msg = sk.EncryptCfb(Encoding.UTF8.GetBytes("SIG INVALID"), sk.IV, PaddingMode.PKCS7); msg = sk.EncryptCfb(Encoding.UTF8.GetBytes("SIG INVALID"), sk.IV, PaddingMode.PKCS7);
log.Decrypted("Fail Register", Encoding.UTF8.GetBytes("SIG INVALID"));
log.Encrypted("Fail Register", msg);
await stream.WriteAsync(msg); await stream.WriteAsync(msg);
} }
} }
@ -172,42 +191,51 @@ public class Program
{ {
// verify login // verify login
clientPhone = Utils.BytesToNumber(msg[3..11]); clientPhone = Utils.BytesToNumber(msg[3..11]);
log.Client = clientPhone;
counter = IncrementCounter(msg[2]); counter = IncrementCounter(msg[2]);
if (!Data.Keys.TryGetValue(clientPhone, out RSA? clientKey)) if (!Data.Keys.TryGetValue(clientPhone, out RSA? clientKey))
{ {
stream.Close(); stream.Close();
client.Close(); client.Close();
Write(id, $"Client claims to be {clientPhone}, but could not find key in records"); log.System($"Client claims to be {clientPhone}, but could not find key in records");
return; return;
} }
byte[] challenge = new byte[16]; byte[] challenge = new byte[16];
Rand.NextBytes(challenge); Rand.NextBytes(challenge);
Write(id, $"Sending challenge: {Convert.ToBase64String(challenge)}"); log.Decrypted("Challenge", challenge);
byte[] response = sk.EncryptCfb(challenge, sk.IV, PaddingMode.None); byte[] response = sk.EncryptCfb(challenge, sk.IV, PaddingMode.None);
log.Encrypted("Challenge", response);
await stream.WriteAsync(response); await stream.WriteAsync(response);
len = await stream.ReadAsync(buffer); len = await stream.ReadAsync(buffer);
log.Encrypted("Challenge Response", buffer[..len]);
msg = sk.DecryptCfb(buffer[..MSG_LEN], sk.IV, PaddingMode.None); msg = sk.DecryptCfb(buffer[..MSG_LEN], sk.IV, PaddingMode.None);
Write(id, Request.RequestToString(msg)); log.Decrypted("Challenge Message", msg);
log.Message(Request.RequestToString(msg));
if (msg[2] != counter) if (msg[2] != counter)
{ {
client.Close(); client.Close();
Write(id, $"Invalid counter in login response, quitting"); log.System($"Invalid counter in login response, quitting");
return; return;
} }
counter = IncrementCounter(counter); counter = IncrementCounter(counter);
byte[] sig = buffer[MSG_LEN..len]; byte[] sig = buffer[MSG_LEN..len];
Write(id, $"Got challenge signature, length: {len - MSG_LEN}, client says: {BitConverter.ToInt32(msg, 3)}"); // sig wasnt encrypted so this print is somewhat redundant, but i think its still useful to see the sig alone
Write(id, $"Sig: {Convert.ToBase64String(sig)}"); log.Decrypted("Challenge Sig", sig);
bool valid = clientKey.VerifyData(challenge, sig, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); bool valid = clientKey.VerifyData(challenge, sig, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
if (valid) if (valid)
{ {
log.System("Client verification complete");
response = sk.EncryptCfb(Encoding.UTF8.GetBytes("OK"), sk.IV, PaddingMode.PKCS7); response = sk.EncryptCfb(Encoding.UTF8.GetBytes("OK"), sk.IV, PaddingMode.PKCS7);
log.Decrypted("Challenge Response", Encoding.UTF8.GetBytes("OK"));
log.Encrypted("Challenge Response", response);
await stream.WriteAsync(response); await stream.WriteAsync(response);
} }
else else
{ {
Write(id, "Client failed verification, invalid signature"); log.System("Client failed verification, invalid signature");
response = sk.EncryptCfb(Encoding.UTF8.GetBytes("INVALID SIG"), sk.IV, PaddingMode.PKCS7); response = sk.EncryptCfb(Encoding.UTF8.GetBytes("INVALID SIG"), sk.IV, PaddingMode.PKCS7);
log.Decrypted("Challenge Response", Encoding.UTF8.GetBytes("INVALID SIG"));
log.Encrypted("Challenge Response", response);
await stream.WriteAsync(response); await stream.WriteAsync(response);
client.Close(); client.Close();
return; return;
@ -216,7 +244,7 @@ public class Program
else else
{ {
// invalid connection, quit // invalid connection, quit
Write(id, "Client didnt register or login as first message"); log.System("Client didnt register or login as first message");
client.Dispose(); client.Dispose();
return; return;
} }
@ -228,12 +256,14 @@ public class Program
// while the client is connected, simply read messages from the client and handle accordingly, // while the client is connected, simply read messages from the client and handle accordingly,
// either by getting new messages for other ppl, or sending back keys/pending messages // either by getting new messages for other ppl, or sending back keys/pending messages
len = await stream.ReadAsync(buffer); len = await stream.ReadAsync(buffer);
log.Encrypted("Request", buffer[..len]);
msg = sk.DecryptCfb(buffer[..MSG_LEN], sk.IV, PaddingMode.None); msg = sk.DecryptCfb(buffer[..MSG_LEN], sk.IV, PaddingMode.None);
Write(id, Request.RequestToString(msg)); log.Decrypted("Request message", msg);
log.Message(Request.RequestToString(msg));
// verify that the counter message is correct // verify that the counter message is correct
if (msg[0] != 0 || msg[2] != counter) if (msg[0] != 0 || msg[2] != counter)
{ {
msg = sk.EncryptCfb(Encoding.UTF8.GetBytes("DUPLICATE"), sk.IV, PaddingMode.PKCS7); msg = sk.EncryptCfb(Encoding.UTF8.GetBytes("INVALID REQUEST"), sk.IV, PaddingMode.PKCS7);
await stream.WriteAsync(msg); await stream.WriteAsync(msg);
continue; continue;
} }
@ -244,7 +274,6 @@ public class Program
byte[] msgsLens = Enumerable.Repeat<byte>(0, 16).ToArray(); // 128 bits byte[] msgsLens = Enumerable.Repeat<byte>(0, 16).ToArray(); // 128 bits
// get 15 messages, last byte will indicate if there are more // get 15 messages, last byte will indicate if there are more
List<byte[]> msgs = Data.GetMessages(clientPhone, 7) ?? []; List<byte[]> msgs = Data.GetMessages(clientPhone, 7) ?? [];
Write(id, $"Got {msgs.Count} messages");
byte[] msgsBytes = new byte[msgs.Select(m => m.Length).Sum()]; byte[] msgsBytes = new byte[msgs.Select(m => m.Length).Sum()];
int msgsbytesIndex = 0; int msgsbytesIndex = 0;
for (int i = 0; i < msgs.Count; i += 1) for (int i = 0; i < msgs.Count; i += 1)
@ -260,8 +289,11 @@ public class Program
} }
msgsLens[15] = Data.PeekMessages(clientPhone) ? (byte)1 : (byte)0; msgsLens[15] = Data.PeekMessages(clientPhone) ? (byte)1 : (byte)0;
// only need to encrypt the lengths of the messages, as the messages themselves are encrypted // only need to encrypt the lengths of the messages, as the messages themselves are encrypted
log.Decrypted("GetMessages", msgsLens);
msgsLens = sk.EncryptCfb(msgsLens, sk.IV, PaddingMode.None); msgsLens = sk.EncryptCfb(msgsLens, sk.IV, PaddingMode.None);
log.Encrypted("GetMessages", msgsLens);
byte[] finalPayload = [.. msgsLens, .. msgsBytes]; byte[] finalPayload = [.. msgsLens, .. msgsBytes];
log.Encrypted("GetMessages Final", finalPayload);
await stream.WriteAsync(finalPayload); await stream.WriteAsync(finalPayload);
break; break;
case RequestType.GetUserKey: case RequestType.GetUserKey:
@ -270,13 +302,17 @@ public class Program
if (key != null) if (key != null)
{ {
msg = [0, .. key.ExportRSAPublicKey()]; msg = [0, .. key.ExportRSAPublicKey()];
log.Decrypted("GetUserKey", msg);
msg = sk.EncryptCfb(msg, sk.IV, PaddingMode.PKCS7); msg = sk.EncryptCfb(msg, sk.IV, PaddingMode.PKCS7);
log.Encrypted("GetUserKey", msg);
await stream.WriteAsync(msg); await stream.WriteAsync(msg);
} }
else else
{ {
msg = [1, .. Encoding.UTF8.GetBytes("USER DOES NOT EXIST")]; msg = [1, .. Encoding.UTF8.GetBytes("USER DOES NOT EXIST")];
log.Decrypted("GetUserKey", msg);
msg = sk.EncryptCfb(msg, sk.IV, PaddingMode.PKCS7); msg = sk.EncryptCfb(msg, sk.IV, PaddingMode.PKCS7);
log.Encrypted("GetUserKey", msg);
await stream.WriteAsync(msg); await stream.WriteAsync(msg);
} }
break; break;
@ -285,12 +321,13 @@ public class Program
int msgLen = BitConverter.ToInt32(msg, 11); int msgLen = BitConverter.ToInt32(msg, 11);
if (msgLen != (len - MSG_LEN)) if (msgLen != (len - MSG_LEN))
{ {
Write(id, $"Got message to {recv} of length {len - MSG_LEN} but expected {msgLen}"); log.System($"Got message to {recv} of length {len - MSG_LEN} but expected {msgLen}");
} }
byte[] clientMsg = buffer[MSG_LEN..(msgLen + MSG_LEN)]; byte[] clientMsg = buffer[MSG_LEN..(msgLen + MSG_LEN)];
log.Encrypted("SendMessage message", clientMsg);
// simply add the clientMsg to the "Data" // simply add the clientMsg to the "Data"
bool added = Data.AddMessage(recv, clientMsg); bool added = Data.AddMessage(recv, clientMsg);
Write(id, $"Added message to {recv} of length {msgLen}: {added}"); log.System($"Added message to {recv} of length {msgLen}: {added}");
break; break;
default: default:
msg = sk.EncryptCfb(Encoding.UTF8.GetBytes("INVALID REQUEST"), sk.IV, PaddingMode.PKCS7); msg = sk.EncryptCfb(Encoding.UTF8.GetBytes("INVALID REQUEST"), sk.IV, PaddingMode.PKCS7);
@ -303,8 +340,8 @@ public class Program
} }
catch (Exception ex) catch (Exception ex)
{ {
Write(id, $"Client failed with error {ex.Message}"); log.System($"Client failed with error {ex.Message}");
Write(id, $"Stack: {ex.StackTrace}"); log.System($"Stack: {ex.StackTrace}");
} }
client.Dispose(); client.Dispose();
@ -315,14 +352,8 @@ public class Program
return counter == byte.MaxValue ? (byte)0 : (byte)(counter + 1); return counter == byte.MaxValue ? (byte)0 : (byte)(counter + 1);
} }
static async Task Send6DigitCodeInSecureChannel(NetworkStream stream, byte[] code) static async Task SendBySecureChannel(NetworkStream stream, byte[] code)
{ {
await stream.WriteAsync(code); await stream.WriteAsync(code);
} }
/// Helper log message so it would print both the time and the id
static void Write(int id, string Message)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {id} - {Message}");
}
} }

1
server_data.json Normal file
View file

@ -0,0 +1 @@
{"Keys":{"0000":{"KeyExchangeAlgorithm":"RSA","SignatureAlgorithm":"RSA","KeySize":1024,"LegalKeySizes":[{"MinSize":512,"MaxSize":16384,"SkipSize":8}]}},"Messages":{"0000":["FfjoDxhjwTeAg8+SLXd+JR53uxIWR/I03/vTeagDX14oLqyxMgSoIKmsgDELv9aSOipN0ZcQwSeaYu6EGIZEn33r8bNpDX7rSSaztt9OEFINlAMQAMAzPuKvGB4nDg7MyDBmHXGxlvYLMTM93WoIWb2BWk3Nz/rpd3fB/Wt7/yXoNlK9hTvPbVcFMMgntVs1gjSuHHx/RLrBZ/7aoEkpEB1HM1NkvnAccYQGluv+7FES86xI8AXWIuU3iBDM3LhUUPZxmFWjLToiMqFtyp8BMyRH7HUufbzQpw+BpZMGo33LekitVhG42LHQDebsjdvWUFutLVDDdwG7sPPmzKBYRQ=="]}}