got login working
This commit is contained in:
parent
04a4f7ece8
commit
2aa4a86e5f
5 changed files with 108 additions and 100 deletions
79
README.md
79
README.md
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue