Files
Relay/RelayClient/MainPage.xaml.cs
2026-03-21 04:45:49 -04:00

204 lines
6.2 KiB
C#

using RelayClient.Crypto;
using RelayClient.Models;
using WebSocketSharp;
using System.Text.Json;
namespace RelayClient;
public partial class MainPage : ContentPage
{
private readonly string _username;
private readonly WebSocket wsc;
private string? _serverPublicKey;
public MainPage(string username)
{
InitializeComponent();
_username = username;
UserLabel.Text = $"Logged in as: {_username}";
if (!KeyStorage.HasKeys(_username))
{
var keys = E2EeHelper.GenerateRsaKeyPair();
KeyStorage.SavePrivateKey(_username, keys.privateKey);
KeyStorage.SavePublicKey(_username, keys.publicKey);
}
wsc = new WebSocket("ws://localhost:1337/");
wsc.OnMessage += WscOnMessage;
wsc.Connect();
var publicKey = KeyStorage.LoadPublicKey(_username);
wsc.Send($"REGISTER_KEY|{_username}|{publicKey}");
wsc.Send("GET_SERVER_KEY");
wsc.Send($"GET_HISTORY|{_username}");
}
private void SendButton_OnClicked(object? sender, EventArgs e)
{
SendMessage();
}
private void MessageEntry_OnCompleted(object? sender, EventArgs e)
{
SendMessage();
}
private void SendMessage()
{
var text = MessageEntry.Text?.Trim();
if (string.IsNullOrWhiteSpace(text))
return;
if (string.IsNullOrWhiteSpace(_serverPublicKey))
{
Console.WriteLine("Server public key not loaded yet.");
return;
}
var encrypted = E2EeHelper.EncryptForRecipient(text, _serverPublicKey);
var payload = new SocketEncryptedMessage
{
Type = "client_encrypted_chat",
SenderUsername = _username,
CipherText = encrypted.CipherText,
Nonce = encrypted.Nonce,
Tag = encrypted.Tag,
EncryptedKey = encrypted.EncryptedKey
};
var json = JsonSerializer.Serialize(payload);
wsc.Send(json);
Console.WriteLine($"[{_username}] sent encrypted message.");
MessageEntry.Text = string.Empty;
MessageEntry.Focus();
}
private void WscOnMessage(object? sender, MessageEventArgs e)
{
if (e.Data.StartsWith("SERVER:REGISTERED_KEY:"))
{
Console.WriteLine(e.Data);
return;
}
Console.WriteLine($"[{_username}] RAW WS DATA: {e.Data}");
try
{
using var doc = JsonDocument.Parse(e.Data);
var root = doc.RootElement;
if (!root.TryGetProperty("Type", out var typeElement))
return;
var type = typeElement.GetString();
if (type == "server_public_key")
{
var serverKeyMessage = JsonSerializer.Deserialize<ServerPublicKeyMessage>(e.Data);
if (serverKeyMessage is not null)
{
_serverPublicKey = serverKeyMessage.PublicKey;
Console.WriteLine($"[{_username}] loaded server public key.");
}
return;
}
if (type != "encrypted_chat")
return;
var payload = JsonSerializer.Deserialize<SocketEncryptedMessage>(e.Data);
if (payload is null)
return;
if (payload.RecipientUsername != _username)
return;
Console.WriteLine($"[{_username}] received encrypted payload for {payload.RecipientUsername}");
var privateKey = KeyStorage.LoadPrivateKey(_username);
var decryptedText = E2EeHelper.DecryptForRecipient(
new EncryptedPayload
{
CipherText = payload.CipherText,
Nonce = payload.Nonce,
Tag = payload.Tag,
EncryptedKey = payload.EncryptedKey
},
privateKey
);
Console.WriteLine($"[{_username}] decrypted message from {payload.SenderUsername}: {decryptedText}");
var message = new ChatMessage
{
SenderUsername = payload.SenderUsername,
Text = decryptedText,
Timestamp = DateTime.Now
};
MainThread.BeginInvokeOnMainThread(async () =>
{
bool isOwnMessage = message.SenderUsername == _username;
var bubble = new Border
{
StrokeThickness = 1,
Padding = 10,
Margin = isOwnMessage
? new Thickness(40, 0, 0, 0)
: new Thickness(0, 0, 40, 0),
HorizontalOptions = isOwnMessage
? LayoutOptions.End
: LayoutOptions.Start,
Content = new VerticalStackLayout
{
Spacing = 2,
Children =
{
new Label
{
Text = message.SenderUsername,
FontAttributes = FontAttributes.Bold,
FontSize = 12
},
new Label
{
Text = message.Text,
FontSize = 14
},
new Label
{
Text = message.Timestamp.ToString("h:mm tt"),
FontSize = 10
}
}
}
};
MessagesLayout.Children.Add(bubble);
await MessagesScrollView.ScrollToAsync(MessagesLayout, ScrollToPosition.End, true);
});
}
catch (Exception ex)
{
Console.WriteLine($"[{_username}] failed to process websocket message: {ex.Message}");
}
}
protected override void OnDisappearing()
{
wsc.OnMessage -= WscOnMessage;
wsc.Close();
base.OnDisappearing();
}
}