Update: Added RelayServer Logic
This commit is contained in:
@@ -2,14 +2,12 @@
|
|||||||
|
|
||||||
namespace RelayServer.Models;
|
namespace RelayServer.Models;
|
||||||
|
|
||||||
public class Messages : Record
|
public class ChannelMessages : Record
|
||||||
{
|
{
|
||||||
public required string ConversationId { get; set; }
|
public required string ChannelId { get; set; }
|
||||||
public required string SenderUserId { get; set; }
|
public required string SenderUserId { get; set; }
|
||||||
public required string RecipientUserId { get; set; }
|
|
||||||
public required string CipherText { get; set; }
|
public required string CipherText { get; set; }
|
||||||
public required string Nonce { get; set; }
|
public required string Nonce { get; set; }
|
||||||
public required string Tag { get; set; }
|
public required string Tag { get; set; }
|
||||||
public required string EncryptedKey { get; set; }
|
|
||||||
public required DateTime CreatedAt { get; set; }
|
public required DateTime CreatedAt { get; set; }
|
||||||
}
|
}
|
||||||
9
RelayServer/Models/Channels.cs
Normal file
9
RelayServer/Models/Channels.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using SurrealDb.Net.Models;
|
||||||
|
|
||||||
|
namespace RelayServer.Models;
|
||||||
|
|
||||||
|
public class Channels : Record
|
||||||
|
{
|
||||||
|
public required string Name { get; set; }
|
||||||
|
public required DateTime CreatedAt { get; set; }
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using SurrealDb.Net.Models;
|
|
||||||
|
|
||||||
namespace RelayServer.Models;
|
|
||||||
|
|
||||||
public class Conversations : Record
|
|
||||||
{
|
|
||||||
public required string CreatedByUserId { get; set; }
|
|
||||||
public required DateTime CreatedAt { get; set; }
|
|
||||||
public required DateTime UpdatedAt { get; set; }
|
|
||||||
public string? Title { get; set; }
|
|
||||||
public bool IsDirectMessage { get; set; }
|
|
||||||
}
|
|
||||||
@@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
namespace RelayServer.Models;
|
namespace RelayServer.Models;
|
||||||
|
|
||||||
public class UserKeys : Record
|
public class ServerEncryptionKeys : Record
|
||||||
{
|
{
|
||||||
public required string UserId { get; set; }
|
public required string KeyBase64 { get; set; }
|
||||||
public required string PublicKey { get; set; }
|
|
||||||
public required DateTime CreatedAt { get; set; }
|
public required DateTime CreatedAt { get; set; }
|
||||||
public required DateTime UpdatedAt { get; set; }
|
public required DateTime UpdatedAt { get; set; }
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace RelayServer.Models;
|
namespace RelayServer.Models;
|
||||||
|
|
||||||
public class ConversationMembers : Record
|
public class ServerMembers : Record
|
||||||
{
|
{
|
||||||
public required string ConversationId { get; set; }
|
|
||||||
public required string UserId { get; set; }
|
public required string UserId { get; set; }
|
||||||
public required DateTime JoinedAt { get; set; }
|
public required DateTime JoinedAt { get; set; }
|
||||||
|
public bool IsOwner { get; set; }
|
||||||
}
|
}
|
||||||
10
RelayServer/Models/Servers.cs
Normal file
10
RelayServer/Models/Servers.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using SurrealDb.Net.Models;
|
||||||
|
|
||||||
|
namespace RelayServer.Models;
|
||||||
|
|
||||||
|
public class Servers : Record
|
||||||
|
{
|
||||||
|
public required string Name { get; set; }
|
||||||
|
public required string OwnerUserId { get; set; }
|
||||||
|
public required DateTime CreatedAt { get; set; }
|
||||||
|
}
|
||||||
@@ -1 +1,152 @@
|
|||||||
Console.WriteLine("Hello, World!");
|
using System.Text.Json;
|
||||||
|
using RelayServer.Models;
|
||||||
|
using RelayServer.Services;
|
||||||
|
|
||||||
|
var surrealService = new SurrealService();
|
||||||
|
var coreClient = new CoreClientService();
|
||||||
|
var cryptoService = new ChannelCryptoService();
|
||||||
|
|
||||||
|
await using var db = await surrealService.ConnectAsync();
|
||||||
|
|
||||||
|
var keeper = await coreClient.GetUserByUsernameAsync("Keeper317");
|
||||||
|
var kira = await coreClient.GetUserByUsernameAsync("Ru_Kira");
|
||||||
|
|
||||||
|
if (keeper is null || kira is null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("One or more required users do not exist in RelayCore.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keeper.Licensed || !kira.Licensed)
|
||||||
|
{
|
||||||
|
Console.WriteLine("One or more required users are not licensed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"Core verified user: {keeper.Username}");
|
||||||
|
Console.WriteLine($"Core verified user: {kira.Username}");
|
||||||
|
|
||||||
|
var server = await db.Create("servers", new Servers
|
||||||
|
{
|
||||||
|
Name = "Test Server",
|
||||||
|
OwnerUserId = kira.Id,
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
|
||||||
|
Console.WriteLine($"Server created: {ToJsonString(server)}");
|
||||||
|
|
||||||
|
var keeperMember = await db.Create("server_members", new ServerMembers
|
||||||
|
{
|
||||||
|
UserId = keeper.Id,
|
||||||
|
JoinedAt = DateTime.UtcNow,
|
||||||
|
IsOwner = true
|
||||||
|
});
|
||||||
|
|
||||||
|
var kiraMember = await db.Create("server_members", new ServerMembers
|
||||||
|
{
|
||||||
|
UserId = kira.Id,
|
||||||
|
JoinedAt = DateTime.UtcNow,
|
||||||
|
IsOwner = false
|
||||||
|
});
|
||||||
|
|
||||||
|
Console.WriteLine("Server members created.");
|
||||||
|
|
||||||
|
var channel = await db.Create("channels", new Channels
|
||||||
|
{
|
||||||
|
Name = "general",
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
|
||||||
|
Console.WriteLine($"Channel created: {ToJsonString(channel)}");
|
||||||
|
|
||||||
|
var channelId = GetRecordId(channel.Id);
|
||||||
|
Console.WriteLine($"Resolved channelId: {channelId}");
|
||||||
|
|
||||||
|
Console.WriteLine($"Channel created: {ToJsonString(channel)}");
|
||||||
|
|
||||||
|
var keyBase64 = cryptoService.GenerateKey();
|
||||||
|
|
||||||
|
var serverKey = await db.Create("server_encryption_keys", new ServerEncryptionKeys
|
||||||
|
{
|
||||||
|
KeyBase64 = keyBase64,
|
||||||
|
CreatedAt = DateTime.UtcNow,
|
||||||
|
UpdatedAt = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
|
||||||
|
Console.WriteLine("Server encryption key created.");
|
||||||
|
|
||||||
|
var encrypted = cryptoService.Encrypt("hello from Keeper317 in #general", keyBase64);
|
||||||
|
|
||||||
|
var savedMessage = await db.Create("channel_messages", new ChannelMessages
|
||||||
|
{
|
||||||
|
ChannelId = channelId,
|
||||||
|
SenderUserId = keeper.Id,
|
||||||
|
CipherText = encrypted.cipherText,
|
||||||
|
Nonce = encrypted.nonce,
|
||||||
|
Tag = encrypted.tag,
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
|
||||||
|
Console.WriteLine($"Encrypted message saved: {ToJsonString(savedMessage)}");
|
||||||
|
|
||||||
|
var decrypted = cryptoService.Decrypt(
|
||||||
|
savedMessage.CipherText,
|
||||||
|
savedMessage.Nonce,
|
||||||
|
savedMessage.Tag,
|
||||||
|
keyBase64
|
||||||
|
);
|
||||||
|
|
||||||
|
var storedMessages = await db.Select<ChannelMessages>("channel_messages");
|
||||||
|
|
||||||
|
Console.WriteLine("Stored DB messages:");
|
||||||
|
Console.WriteLine(ToJsonString(storedMessages));
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine($"Decrypted message: {decrypted}");
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("Simulating Kira reading #general...");
|
||||||
|
|
||||||
|
var kiraVisibleMessages = storedMessages
|
||||||
|
.Where(m => m.ChannelId == channelId)
|
||||||
|
.OrderBy(m => m.CreatedAt)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var msg in kiraVisibleMessages)
|
||||||
|
{
|
||||||
|
var plainText = cryptoService.Decrypt(
|
||||||
|
msg.CipherText,
|
||||||
|
msg.Nonce,
|
||||||
|
msg.Tag,
|
||||||
|
keyBase64
|
||||||
|
);
|
||||||
|
|
||||||
|
Console.WriteLine($"Kira reads message from {msg.SenderUserId}: {plainText}");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
static string ToJsonString(object? obj)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Serialize(obj, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static string GetRecordId(object? id)
|
||||||
|
{
|
||||||
|
if (id is null)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var json = JsonSerializer.Serialize(id);
|
||||||
|
|
||||||
|
using var doc = JsonDocument.Parse(json);
|
||||||
|
|
||||||
|
var root = doc.RootElement;
|
||||||
|
|
||||||
|
var recordId = root.GetProperty("Id").GetString() ?? string.Empty;
|
||||||
|
var table = root.GetProperty("Table").GetString() ?? string.Empty;
|
||||||
|
|
||||||
|
return $"{table}:{recordId}";
|
||||||
|
}
|
||||||
44
RelayServer/Services/ChannelCryptoService.cs
Normal file
44
RelayServer/Services/ChannelCryptoService.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace RelayServer.Services;
|
||||||
|
|
||||||
|
public sealed class ChannelCryptoService
|
||||||
|
{
|
||||||
|
public string GenerateKey()
|
||||||
|
{
|
||||||
|
return Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
public (string cipherText, string nonce, string tag) Encrypt(string plainText, string keyBase64)
|
||||||
|
{
|
||||||
|
var key = Convert.FromBase64String(keyBase64);
|
||||||
|
var nonce = RandomNumberGenerator.GetBytes(12);
|
||||||
|
var plainBytes = Encoding.UTF8.GetBytes(plainText);
|
||||||
|
var cipherBytes = new byte[plainBytes.Length];
|
||||||
|
var tag = new byte[16];
|
||||||
|
|
||||||
|
using var aes = new AesGcm(key, 16);
|
||||||
|
aes.Encrypt(nonce, plainBytes, cipherBytes, tag);
|
||||||
|
|
||||||
|
return (
|
||||||
|
Convert.ToBase64String(cipherBytes),
|
||||||
|
Convert.ToBase64String(nonce),
|
||||||
|
Convert.ToBase64String(tag)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Decrypt(string cipherTextBase64, string nonceBase64, string tagBase64, string keyBase64)
|
||||||
|
{
|
||||||
|
var key = Convert.FromBase64String(keyBase64);
|
||||||
|
var nonce = Convert.FromBase64String(nonceBase64);
|
||||||
|
var tag = Convert.FromBase64String(tagBase64);
|
||||||
|
var cipherBytes = Convert.FromBase64String(cipherTextBase64);
|
||||||
|
var plainBytes = new byte[cipherBytes.Length];
|
||||||
|
|
||||||
|
using var aes = new AesGcm(key, 16);
|
||||||
|
aes.Decrypt(nonce, cipherBytes, tag, plainBytes);
|
||||||
|
|
||||||
|
return Encoding.UTF8.GetString(plainBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
RelayServer/Services/ChannelMessageService.cs
Normal file
6
RelayServer/Services/ChannelMessageService.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace RelayServer.Services;
|
||||||
|
|
||||||
|
public class ChannelMessageService
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
16
RelayServer/Services/CoreClientService.cs
Normal file
16
RelayServer/Services/CoreClientService.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
namespace RelayServer.Services;
|
||||||
|
|
||||||
|
public sealed class CoreClientService
|
||||||
|
{
|
||||||
|
public Task<CoreUser?> GetUserByUsernameAsync(string username)
|
||||||
|
{
|
||||||
|
return Task.FromResult<CoreUser?>(username switch
|
||||||
|
{
|
||||||
|
"Keeper317" => new CoreUser("users:keeper317", "Keeper317", true),
|
||||||
|
"Ru_Kira" => new CoreUser("users:ru_kira", "Ru_Kira", true),
|
||||||
|
_ => null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record CoreUser(string Id, string Username, bool Licensed);
|
||||||
6
RelayServer/Services/ServerBootstrapService.cs
Normal file
6
RelayServer/Services/ServerBootstrapService.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace RelayServer.Services;
|
||||||
|
|
||||||
|
public class ServerBootstrapService
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
21
RelayServer/Services/SurrealService.cs
Normal file
21
RelayServer/Services/SurrealService.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using SurrealDb.Net;
|
||||||
|
using SurrealDb.Net.Models.Auth;
|
||||||
|
|
||||||
|
namespace RelayServer.Services;
|
||||||
|
|
||||||
|
public sealed class SurrealService
|
||||||
|
{
|
||||||
|
public async Task<SurrealDbClient> ConnectAsync()
|
||||||
|
{
|
||||||
|
var db = new SurrealDbClient("ws://127.0.0.1:8000/rpc");
|
||||||
|
|
||||||
|
await db.SignIn(new RootAuth
|
||||||
|
{
|
||||||
|
Username = "root",
|
||||||
|
Password = "secret"
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.Use("test", "test");
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user