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:
|
# Project - TODO:
|
||||||
|
|
||||||
[x] implement SendMessage at the server
|
[ ] Do print stuff for EVERYTHING (with colors preferably)
|
||||||
[ ] implement Login
|
[ ] rewrite the protocol.md
|
||||||
[ ] client
|
[ ] export everything and upload it
|
||||||
[ ] server
|
[ ] get a nice drink to celebrate
|
||||||
[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)
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -15,9 +16,9 @@ namespace Client;
|
||||||
|
|
||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
static byte counter = 0;
|
|
||||||
static string User = "";
|
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)
|
static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
|
@ -40,11 +41,17 @@ public class Program
|
||||||
// First contact init
|
// First contact init
|
||||||
bool needsRegister = pubKey == privKey || args.Any(a => new[] { "-fr", "--force-register" }.Contains(a));
|
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
|
||||||
|
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)
|
if (needsRegister)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await RegisterClient(User, pubKey, privKey, serverKey, sk, stream);
|
await RegisterClient(User, pubKey, privKey, sk, stream);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -56,25 +63,46 @@ public class Program
|
||||||
}
|
}
|
||||||
else
|
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);
|
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("Attempting to register with public key:");
|
||||||
Console.WriteLine(pub.ExportRSAPublicKeyPem());
|
Console.WriteLine(pub.ExportRSAPublicKeyPem());
|
||||||
// Generate aes key and send it forward
|
// Generate aes key and send it forward
|
||||||
Console.WriteLine($"Session key: {string.Join(' ', sk.Key)}");
|
Console.WriteLine($"Session key: {string.Join(' ', sk.Key)}");
|
||||||
Console.WriteLine($"Session IV: {string.Join(' ', sk.IV)}");
|
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
|
// Generate the Register msg
|
||||||
Console.WriteLine("Sending rsa public key thing");
|
Console.WriteLine("Sending rsa public key thing");
|
||||||
|
@ -130,6 +158,10 @@ public class Program
|
||||||
Console.WriteLine("Registration process complete");
|
Console.WriteLine("Registration process complete");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Failed reigstreation: " + r);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,10 @@ public enum RequestType
|
||||||
Register = 1,
|
Register = 1,
|
||||||
ConfirmRegister = 2,
|
ConfirmRegister = 2,
|
||||||
Login = 3,
|
Login = 3,
|
||||||
GetMessages = 4,
|
ConfirmLogin = 4,
|
||||||
GetUserKey = 5,
|
GetMessages = 5,
|
||||||
SendMessage = 6,
|
GetUserKey = 6,
|
||||||
|
SendMessage = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Request
|
public static class Request
|
||||||
|
@ -62,8 +63,11 @@ public static class Request
|
||||||
break;
|
break;
|
||||||
case RequestType.Login:
|
case RequestType.Login:
|
||||||
phone = Utils.BytesToNumber(Request[3..11]);
|
phone = Utils.BytesToNumber(Request[3..11]);
|
||||||
sigLen = BitConverter.ToInt16(Request, 11);
|
res += $"Phone: {phone}";
|
||||||
res += $"Phone: {phone}, signature length: {sigLen}";
|
break;
|
||||||
|
case RequestType.ConfirmLogin:
|
||||||
|
sigLen = BitConverter.ToInt16(Request, 3);
|
||||||
|
res += $"signature length: {sigLen}";
|
||||||
break;
|
break;
|
||||||
case RequestType.GetMessages:
|
case RequestType.GetMessages:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -52,8 +52,10 @@ to the server.
|
||||||
data: Phone - 8 bytes, RSA key size (payload length) - 2 bytes
|
data: Phone - 8 bytes, RSA key size (payload length) - 2 bytes
|
||||||
- ConfirmRegister (signed & encrypted 6 digit code)
|
- ConfirmRegister (signed & encrypted 6 digit code)
|
||||||
data: 6 bytes for the 6 digit code (can use less but it will be padding otherwise)
|
data: 6 bytes for the 6 digit code (can use less but it will be padding otherwise)
|
||||||
- Login (signed hash):
|
- Login:
|
||||||
data: 8 bytes of user's phone, signed SHA length - 2 bytes
|
data: 8 bytes of user's phone
|
||||||
|
- ConfirmLogin (signed hash):
|
||||||
|
data: hash length
|
||||||
- GetMessages:
|
- GetMessages:
|
||||||
data: EMPTY
|
data: EMPTY
|
||||||
- GetUserKey (dont think it needs any signing or encryption technically, as it is very
|
- 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.Net.Sockets;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using lib;
|
using lib;
|
||||||
using server;
|
using server;
|
||||||
|
@ -18,7 +19,7 @@ public class Program
|
||||||
const int MSG_LEN = 16; // msg len is 128 bits = 16 bytes
|
const int MSG_LEN = 16; // msg len is 128 bits = 16 bytes
|
||||||
|
|
||||||
static readonly Data Data = new();
|
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()
|
static async Task Main()
|
||||||
{
|
{
|
||||||
|
@ -110,12 +111,12 @@ public class Program
|
||||||
Write(id, $"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)Rnd.Next(10),
|
(byte)Rand.Next(10),
|
||||||
(byte)Rnd.Next(10),
|
(byte)Rand.Next(10),
|
||||||
(byte)Rnd.Next(10),
|
(byte)Rand.Next(10),
|
||||||
(byte)Rnd.Next(10),
|
(byte)Rand.Next(10),
|
||||||
(byte)Rnd.Next(10),
|
(byte)Rand.Next(10),
|
||||||
(byte)Rnd.Next(10),
|
(byte)Rand.Next(10),
|
||||||
];
|
];
|
||||||
await Send6DigitCodeInSecureChannel(stream, code);
|
await Send6DigitCodeInSecureChannel(stream, code);
|
||||||
// wait for the code to be back with a key
|
// wait for the code to be back with a key
|
||||||
|
@ -170,7 +171,47 @@ public class Program
|
||||||
else if (msg[1] == (byte)RequestType.Login)
|
else if (msg[1] == (byte)RequestType.Login)
|
||||||
{
|
{
|
||||||
// verify 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
|
else
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue