got login working

This commit is contained in:
Rusty Striker 2025-01-02 18:49:58 +02:00
parent 04a4f7ece8
commit 2aa4a86e5f
Signed by: RustyStriker
GPG key ID: 87E4D691632DFF15
5 changed files with 108 additions and 100 deletions

View file

@ -1,77 +1,6 @@
# Project - TODO:
[x] implement SendMessage at the server
[ ] implement Login
[ ] client
[ ] server
[x] Figure out how to do the messages themselves
[x] Figure out how to pass the signature (i think it must be as a second packet)
[x] implement sending messages properly
[x] implement message acks
## Protocol todo:
[x] Figure out how a message and message ack payload will look
## Misc todo:
[x] Create a Request to String function for easy printing and debugging
## client todo:
[ ] Check for key when turned on
[x] generate key and register if no key is preset, and save it after registration is done
[ ] if key is present, start by establishing connection (which makes sure we are signed in)
[x] use AES to get basic packets from the server
[x] use RSA private key to read normal messages
## Server todo:
[x] Laucnh task for each new connection
[x] use RSA key to get first message and extract AES key
[ ] verify the user using its public RSA key
[x] if it was a register session save the key into the BIG DATA STRUCTURE
[x] Keep lists of incoming messages
(doesnt need to know from who, they are just big blobs of shlomp)
[x] When user asks for incoming messages, make basic packet and append the incoming messages
- last byte is the "how many messages are left" byte
- each byte in the extra data will be the length of the next message, so
if there are 3 messages of length 128, 200, 300 bytes it will be
[128, 200, 300, 0 ...] and the actual position in the payload is easy
to calculate ([128, 200+128=328, 300+328=628, ...])
Register process:
Client Server
Send AES key (sk)
Send Register(pub key)
Get AES
Get Register
Send 6 digit code
Get 6 digit code
(1) Wait for user to input 6 digit code
Send 6 digit code (signed)
Get 6 digit code and verify (code, then sig)
Send OK/NACK
if NACK goto (1)
Login process:
Client Server
Send AES key (sk)
Send Login message (Phone, AES sig)
Get AES key
Verify AES sig with Phone-pub key
if sig is invalid: close connection
else: send stored messages because why not
Usual process get messages:
Client Server
Send GetMessages
Send back messages
Send GotMessages(amount)
[ ] Do print stuff for EVERYTHING (with colors preferably)
[ ] rewrite the protocol.md
[ ] export everything and upload it
[ ] get a nice drink to celebrate

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
@ -15,9 +16,9 @@ namespace Client;
public class Program
{
static byte counter = 0;
static string User = "";
static readonly Random Rand = new();
static readonly Random Rand = new((int)DateTime.Now.Ticks);
static byte counter = (byte)Rand.Next();
static async Task Main(string[] args)
{
@ -40,11 +41,17 @@ public class Program
// First contact init
bool needsRegister = pubKey == privKey || args.Any(a => new[] { "-fr", "--force-register" }.Contains(a));
Aes sk = Aes.Create(); // creates an AES-256 key
// establish secure connection
byte[] skEnc = serverKey.Encrypt([.. sk.Key, .. sk.IV], RSAEncryptionPadding.OaepSHA256);
await stream.WriteAsync(skEnc);
// wait for the server to confirm it recieved the keys
await stream.ReadExactlyAsync(new byte[1]);
if (needsRegister)
{
try
{
await RegisterClient(User, pubKey, privKey, serverKey, sk, stream);
await RegisterClient(User, pubKey, privKey, sk, stream);
}
catch (Exception ex)
{
@ -56,25 +63,46 @@ public class Program
}
else
{
// attempt to login here
// start login process
byte[] msg = Request.CreateRequest(RequestType.Login, ref counter, Utils.NumberToBytes(User));
msg = sk.EncryptCfb(msg, sk.IV, PaddingMode.PKCS7);
await stream.WriteAsync(msg);
byte[] toSign = new byte[16];
await stream.ReadExactlyAsync(toSign, 0, 16);
toSign = sk.DecryptCfb(toSign, sk.IV, PaddingMode.None);
byte[] signed = privKey.SignData(toSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
msg = Request.CreateRequest(RequestType.ConfirmLogin, ref counter, BitConverter.GetBytes(signed.Length));
msg = sk.EncryptCfb(msg, sk.IV, PaddingMode.None);
WriteColor($"Sending sig: {Convert.ToBase64String(signed)}", ConsoleColor.Green);
msg = [.. msg, .. signed];
await stream.WriteAsync(msg);
byte[] buffer = new byte[512];
int len = await stream.ReadAsync(buffer);
byte[] dec = sk.DecryptCfb(buffer[..len], sk.IV, PaddingMode.PKCS7);
string r = Encoding.UTF8.GetString(dec);
if (r == "OK")
{
Console.WriteLine("Login successful");
}
else
{
Console.WriteLine($"Failed login: {r}");
client.Dispose();
return;
}
}
await HandleUserInput(client, stream, sk, privKey);
}
async static Task RegisterClient(string user, RSA pub, RSA priv, RSA server, 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:");
Console.WriteLine(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)}");
byte[] skEnc = server.Encrypt([.. sk.Key, .. sk.IV], RSAEncryptionPadding.OaepSHA256);
await stream.WriteAsync(skEnc);
// wait for the server to confirm it recieved the keys
await stream.ReadExactlyAsync(new byte[1]);
// Generate the Register msg
Console.WriteLine("Sending rsa public key thing");
@ -130,6 +158,10 @@ public class Program
Console.WriteLine("Registration process complete");
break;
}
else
{
Console.WriteLine("Failed reigstreation: " + r);
}
}
}

View file

@ -5,9 +5,10 @@ public enum RequestType
Register = 1,
ConfirmRegister = 2,
Login = 3,
GetMessages = 4,
GetUserKey = 5,
SendMessage = 6,
ConfirmLogin = 4,
GetMessages = 5,
GetUserKey = 6,
SendMessage = 7,
}
public static class Request
@ -62,8 +63,11 @@ public static class Request
break;
case RequestType.Login:
phone = Utils.BytesToNumber(Request[3..11]);
sigLen = BitConverter.ToInt16(Request, 11);
res += $"Phone: {phone}, signature length: {sigLen}";
res += $"Phone: {phone}";
break;
case RequestType.ConfirmLogin:
sigLen = BitConverter.ToInt16(Request, 3);
res += $"signature length: {sigLen}";
break;
case RequestType.GetMessages:
break;

View file

@ -52,8 +52,10 @@ to the server.
data: Phone - 8 bytes, RSA key size (payload length) - 2 bytes
- ConfirmRegister (signed & encrypted 6 digit code)
data: 6 bytes for the 6 digit code (can use less but it will be padding otherwise)
- Login (signed hash):
data: 8 bytes of user's phone, signed SHA length - 2 bytes
- Login:
data: 8 bytes of user's phone
- ConfirmLogin (signed hash):
data: hash length
- GetMessages:
data: EMPTY
- GetUserKey (dont think it needs any signing or encryption technically, as it is very

View file

@ -7,6 +7,7 @@ using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using lib;
using server;
@ -18,7 +19,7 @@ public class Program
const int MSG_LEN = 16; // msg len is 128 bits = 16 bytes
static readonly Data Data = new();
static readonly Random Rnd = new((int)DateTime.Now.Ticks);
static readonly Random Rand = new((int)DateTime.Now.Ticks);
static async Task Main()
{
@ -110,12 +111,12 @@ public class Program
Write(id, $"Imported key is: \n {pub.ExportRSAPublicKeyPem()}\n");
// generate the 6 digit code and send it
byte[] code = [
(byte)Rnd.Next(10),
(byte)Rnd.Next(10),
(byte)Rnd.Next(10),
(byte)Rnd.Next(10),
(byte)Rnd.Next(10),
(byte)Rnd.Next(10),
(byte)Rand.Next(10),
(byte)Rand.Next(10),
(byte)Rand.Next(10),
(byte)Rand.Next(10),
(byte)Rand.Next(10),
(byte)Rand.Next(10),
];
await Send6DigitCodeInSecureChannel(stream, code);
// wait for the code to be back with a key
@ -170,7 +171,47 @@ public class Program
else if (msg[1] == (byte)RequestType.Login)
{
// verify login
// TODO: Login
clientPhone = Utils.BytesToNumber(msg[3..11]);
counter = IncrementCounter(msg[2]);
if (!Data.Keys.TryGetValue(clientPhone, out RSA? clientKey))
{
stream.Close();
client.Close();
Write(id, $"Client claims to be {clientPhone}, but could not find key in records");
return;
}
byte[] challenge = new byte[16];
Rand.NextBytes(challenge);
Write(id, $"Sending challenge: {Convert.ToBase64String(challenge)}");
byte[] response = sk.EncryptCfb(challenge, sk.IV, PaddingMode.None);
await stream.WriteAsync(response);
len = await stream.ReadAsync(buffer);
msg = sk.DecryptCfb(buffer[..MSG_LEN], sk.IV, PaddingMode.None);
Write(id, Request.RequestToString(msg));
if (msg[2] != counter)
{
client.Close();
Write(id, $"Invalid counter in login response, quitting");
return;
}
counter = IncrementCounter(counter);
byte[] sig = buffer[MSG_LEN..len];
Write(id, $"Got challenge signature, length: {len - MSG_LEN}, client says: {BitConverter.ToInt32(msg, 3)}");
Write(id, $"Sig: {Convert.ToBase64String(sig)}");
bool valid = clientKey.VerifyData(challenge, sig, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
if (valid)
{
response = sk.EncryptCfb(Encoding.UTF8.GetBytes("OK"), sk.IV, PaddingMode.PKCS7);
await stream.WriteAsync(response);
}
else
{
Write(id, "Client failed verification, invalid signature");
response = sk.EncryptCfb(Encoding.UTF8.GetBytes("INVALID SIG"), sk.IV, PaddingMode.PKCS7);
await stream.WriteAsync(response);
client.Close();
return;
}
}
else
{