diff --git a/Luski.net/API.cs b/Luski.net/API.cs index 7b239f5..c9bd04d 100644 --- a/Luski.net/API.cs +++ b/Luski.net/API.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Luski.net.Enums; using Luski.net.Structures.Main; using Luski.net.Structures.Public; @@ -19,6 +20,9 @@ public class API { Domain = Domain, ApiVersion = Version, + Storage = new(Domain), + EncryptionHandler = new(Domain), + ServerType = ServerType.Public }; InternalServers.Add(s); return s; @@ -30,6 +34,9 @@ public class API { Domain = Domain, ApiVersion = Version, + Storage = new(Domain), + EncryptionHandler = new(Domain), + ServerType = ServerType.Main }; return MainServer; } diff --git a/Luski.net/ClientEncryption.cs b/Luski.net/ClientEncryption.cs index 4d86d68..61b358f 100755 --- a/Luski.net/ClientEncryption.cs +++ b/Luski.net/ClientEncryption.cs @@ -1,51 +1,19 @@ -using Luski.net.Enums; -using System; +/*using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Net; -using System.Net.Http; using System.Security.Cryptography; using System.Text; using System.Text.Json; -using JacobTechEncryption; namespace Luski.net { public static class ClientEncryption { - internal static string? MyPublicKey; - internal static readonly UnicodeEncoding Encoder = new(); - private static string? myPrivateKey; - internal static bool Generating = false; - internal static bool Generated = false; - private static string? _serverpublickey = null; - internal static string? ofkey = null; - internal static string? outofkey = null; internal static string pw = ""; - public static int NewKeySize = 4096; - - public static void GenerateKeys() - { - if (!Generating) - { - Generating = true; - GenerateNewKeys(out MyPublicKey, out myPrivateKey); - GenerateNewKeys(out outofkey, out ofkey); - Generated = true; - } - } - - public static void GenerateNewKeys(out string Public, out string Private) - { - using RSACryptoServiceProvider r = new(NewKeySize); - Private = r.ToXmlString(true); - Public = r.ToXmlString(false); - return; - } public static class File - { + {/* internal static void SetOfflineKey(string key) { MakeFile("Server.GetKeyFilePath", pw); @@ -54,11 +22,7 @@ namespace Luski.net fileLayout.Save("Server.GetKeyFilePath", pw); } - public static LuskiDataFile GetFile() - { - MakeFile("Server.GetKeyFilePath", pw); - return JsonSerializer.Deserialize(FileString("Server.GetKeyFilePath", pw))!; - } + internal static string? GetOfflineKey() { @@ -66,6 +30,12 @@ namespace Luski.net LuskiDataFile? fileLayout = JsonSerializer.Deserialize(FileString("Server.GetKeyFilePath", pw)); return fileLayout?.OfflineKey; } + + public static LuskiDataFile GetFile() + { + MakeFile("Server.GetKeyFilePath", pw); + return JsonSerializer.Deserialize(FileString("Server.GetKeyFilePath", pw))!; + } private static string FileString(string path, string password) { @@ -112,14 +82,14 @@ namespace Luski.net public static class Channels { - public static string GetKey(long channel) + private static string GetKey(long channel) { LuskiDataFile? fileLayout; IEnumerable? lis; try { #pragma warning disable CS8603 // Possible null reference return. - if (channel == 0) return myPrivateKey; + // if (channel == 0) return myPrivateKey; #pragma warning restore CS8603 // Possible null reference return. MakeFile("Server.GetKeyFilePath", pw); fileLayout = JsonSerializer.Deserialize(FileString("Server.GetKeyFilePath", pw)); @@ -144,7 +114,7 @@ namespace Luski.net try { #pragma warning disable CS8603 // Possible null reference return. - if (channel == 0) return myPrivateKey; + // if (channel == 0) return myPrivateKey; #pragma warning restore CS8603 // Possible null reference return. MakeFile("Server.GetKeyFilePathBr(branch.ToString())", pw); fileLayout = JsonSerializer.Deserialize(FileString("", pw)); @@ -273,8 +243,7 @@ namespace Luski.net public string key { get; set; } = default!; } } - /* - public class AES +public class AES { public static string Encrypt(string path, string Password) { @@ -301,12 +270,10 @@ namespace Luski.net msEncrypt.Dispose(); return p; - /* - - string p = Path.GetTempFileName(); + string p = Path.GetTempFileName(); byte[] salt = new byte[100]; RNGCryptoServiceProvider provider = new(); provider.GetBytes(salt); @@ -378,27 +345,7 @@ namespace Luski.net fsOut.Dispose(); } } -*/ - internal const int PasswordVersion = 0; - internal static byte[] LocalPasswordEncrypt(byte[] Password, int PasswordVersion = PasswordVersion) - { - return PasswordVersion switch - { - 0 => SHA256.Create().ComputeHash(Password), - _ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)), - }; - } - - internal static string RemotePasswordEncrypt(byte[] Password, int PasswordVersion = PasswordVersion) - { - return PasswordVersion switch - { - 0 => Convert.ToBase64String(Encryption.RSA.Encrypt(LocalPasswordEncrypt(Password, PasswordVersion), MyPublicKey)), - _ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)), - }; - } -/* public static byte[] Hash(byte[] data) { return SHA256.Create().ComputeHash(data); @@ -506,6 +453,6 @@ namespace Luski.net } } return datasplitout; - }*/ + } } -} +}*/ diff --git a/Luski.net/Enums/ServerCacheMode.cs b/Luski.net/Enums/CacheMode.cs similarity index 67% rename from Luski.net/Enums/ServerCacheMode.cs rename to Luski.net/Enums/CacheMode.cs index fa9b152..6fc1243 100644 --- a/Luski.net/Enums/ServerCacheMode.cs +++ b/Luski.net/Enums/CacheMode.cs @@ -1,6 +1,6 @@ namespace Luski.net.Enums; -public enum ServerCacheMode : byte +public enum CacheMode : byte { None, Encrypted, diff --git a/Luski.net/Enums/StorageDirectory.cs b/Luski.net/Enums/StorageDirectory.cs new file mode 100644 index 0000000..73266bc --- /dev/null +++ b/Luski.net/Enums/StorageDirectory.cs @@ -0,0 +1,12 @@ +namespace Luski.net.Enums; + +public enum StorageDirectory : byte +{ + ServerInfo, + ServerAssets, + ChannelKeys, + ServerKeys, + Avatars, + ChannelIcons, + Messages +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/ServerStorageInfo.cs b/Luski.net/JsonTypes/ServerStorageInfo.cs new file mode 100644 index 0000000..c54ce8b --- /dev/null +++ b/Luski.net/JsonTypes/ServerStorageInfo.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; +using Luski.net.Enums; + +namespace Luski.net.JsonTypes; + +public class ServerStorageInfo +{ + [JsonInclude] + [JsonPropertyName("cache_mode")] + public CacheMode CacheMode { get; set; } = CacheMode.Encrypted; + [JsonInclude] + [JsonPropertyName("prevent_deletion")] + public bool DontDelete { get; set; } = false; +} + +[JsonSerializable(typeof(ServerStorageInfo))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = true)] +internal partial class ServerStorageInfoContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/Luski.net.csproj b/Luski.net/Luski.net.csproj index 7e53d0b..c2118d4 100755 --- a/Luski.net/Luski.net.csproj +++ b/Luski.net/Luski.net.csproj @@ -13,7 +13,7 @@ https://github.com/JacobTech-com/Luski.net True 1.0.0 - 1.1.3-alpha24 + 1.1.3-alpha25 diff --git a/Luski.net/MainServer.Account.cs b/Luski.net/MainServer.Account.cs index 72d3bf7..421d6fb 100644 --- a/Luski.net/MainServer.Account.cs +++ b/Luski.net/MainServer.Account.cs @@ -13,47 +13,46 @@ using Luski.net.Enums; using Luski.net.JsonTypes; using Luski.net.JsonTypes.WSS; using Luski.net.Structures.Main; -using Luski.net.Structures.Public; using WebSocketSharp; namespace Luski.net; public partial class MainServer { - public void Login(string Email, string Password, System.Threading.CancellationToken CancellationToken) + public void Login(string Username, string Password, CancellationToken CancellationToken) { - Both(Email, Password, CancellationToken); + Both(Username, Password, CancellationToken); } - public void CreateAccount(string Email, string Password, string Username, string PFP, System.Threading.CancellationToken CancellationToken) + public void CreateAccount(string Username, string Password, string Displayname, string PFP, CancellationToken CancellationToken) { - Both(Email, Password, CancellationToken, Username, PFP); + Both(Username, Password, CancellationToken, Displayname, PFP); } - private void Both(string Email, string Password, System.Threading.CancellationToken CancellationToken, string? Username = null, string? pfp = null) + private void Both(string Username, string Password, CancellationToken CancellationToken, string? Displayname = null, string? pfp = null) { - if (!ClientEncryption.Generating) + if (!EncryptionHandler.Generating) { - ClientEncryption.GenerateKeys(); + EncryptionHandler.GenerateKeys(); } - while (!ClientEncryption.Generated) { } + while (!EncryptionHandler.Generated) { } login = true; Login json; List> heads = new() { - new("key", ClientEncryption.MyPublicKey), - new("username", Convert.ToBase64String(Encryption.RSA.Encrypt(Email, ClientEncryption.MyPublicKey, EncoderType.UTF8))), - new("password", ClientEncryption.RemotePasswordEncrypt(Encoding.UTF8.GetBytes(Password))) + new("key", EncryptionHandler.MyPublicKey), + new("username", Convert.ToBase64String(Encryption.RSA.Encrypt(Username, EncryptionHandler.MyPublicKey, EncoderType.UTF8))), + new("password", EncryptionHandler.RemotePasswordEncrypt(Encoding.UTF8.GetBytes(Password))) }; - if (File.Exists("LastPassVer.txt") && int.TryParse(File.ReadAllText("LastPassVer.txt"), out int lpv) && lpv < ClientEncryption.PasswordVersion && lpv >= 0) + if (File.Exists("LastPassVer.txt") && int.TryParse(File.ReadAllText("LastPassVer.txt"), out int lpv) && lpv < EncryptionHandler.PasswordVersion && lpv >= 0) { - heads.Add(new("old_password", ClientEncryption.RemotePasswordEncrypt(Encoding.UTF8.GetBytes(Password), lpv))); + heads.Add(new("old_password", EncryptionHandler.RemotePasswordEncrypt(Encoding.UTF8.GetBytes(Password), lpv))); heads.Add(new("old_version", lpv.ToString())); } if (pfp is not null) { - heads.Add(new("username", Username)); + heads.Add(new("username", Displayname)); json = SendServer( "CreateAccount", pfp, @@ -70,7 +69,7 @@ public partial class MainServer heads.ToArray()).Result; } if (json.Error is not null) throw new Exception($"Luski appears to be down at the current moment: {json.ErrorMessage}"); - if (ClientEncryption.ofkey is null || ClientEncryption.outofkey is null) throw new Exception("Something went wrong generating the offline keys"); + if (EncryptionHandler.OfflinePrivateKey is null || EncryptionHandler.OfflinePublicKey is null) throw new Exception("Something went wrong generating the offline keys"); login = false; if (json is not null && json.Error is null) { @@ -106,31 +105,9 @@ public partial class MainServer } Console.WriteLine("User got"); Console.WriteLine("Insert"); - //User.Email = Email; _ = UpdateStatus(UserStatus.Online, CancellationToken); Console.WriteLine("stat"); - try - { - ClientEncryption.pw = Email.ToLower() + Password; - _ = ClientEncryption.File.GetOfflineKey(); - } - catch - { - try - { - ClientEncryption.pw = Email + Password; - var temp222 = ClientEncryption.File.LuskiDataFile.GetDefualtDataFile(); - ClientEncryption.pw = Email.ToLower() + Password; - if (temp222 is not null) temp222.Save("key.lsk", ClientEncryption.pw); - } - catch - { - Token = null; - Error = null; - ServerOut.Close(); - throw new Exception("The key file you have is getting the wrong pasword. Type your Email in the same way you creaated your account to fix this error."); - } - } + EncryptionHandler.Hash = EncryptionHandler.LocalPasswordEncrypt(Encoding.UTF8.GetBytes(Username.ToLower() + Password)); Console.WriteLine("req offline"); OfflineData offlinedata = GetFromServer("Keys/GetOfflineData", OfflineDataContext.Default.OfflineData, CancellationToken).Result; if (offlinedata.Data is not null && offlinedata.Data.Any()) Console.WriteLine(offlinedata.Data); @@ -145,23 +122,37 @@ public partial class MainServer { Console.WriteLine(okd.channel); Console.WriteLine(okd.key); - ClientEncryption.File.Channels.AddKey(okd.channel, Encoding.UTF8.GetString(Encryption.RSA.Decrypt(Convert.FromBase64String(okd.key), ClientEncryption.File.GetOfflineKey()))); + Storage.SetResourceKey( + StorageDirectory.ChannelKeys, + okd.channel.ToString(), + EncryptionHandler.Hash, + Encoding.UTF8.GetString( + Encryption.RSA.Decrypt( + Convert.FromBase64String(okd.key), + Storage.GetResourceKey( + StorageDirectory.ServerKeys, + "pkey", + EncryptionHandler.Hash + ) + ) + ) + ); } } } Console.WriteLine("lpv"); - System.IO.File.WriteAllText("LastPassVer.txt", ClientEncryption.PasswordVersion.ToString()); - ClientEncryption.File.SetOfflineKey(ClientEncryption.ofkey); + System.IO.File.WriteAllText("LastPassVer.txt", EncryptionHandler.PasswordVersion.ToString()); + Storage.SetResourceKey(StorageDirectory.ServerKeys, "pkey", EncryptionHandler.Hash, EncryptionHandler.OfflinePrivateKey); using HttpClient setkey = new(); setkey.DefaultRequestHeaders.Add("token", Token); - _ = setkey.PostAsync($"https://{Domain}/{ApiVersion}/Keys/SetOfflineKey", new StringContent(ClientEncryption.outofkey)).Result; + _ = setkey.PostAsync($"https://{Domain}/{ApiVersion}/Keys/SetOfflineKey", new StringContent(EncryptionHandler.OfflinePublicKey)).Result; //_ = User.Channels; foreach (var ch in chans) { _ = ch.Members; } - ClientEncryption.outofkey = null; - ClientEncryption.ofkey = null; + EncryptionHandler.OfflinePublicKey = null; + EncryptionHandler.OfflinePrivateKey = null; } else throw new Exception(json?.ErrorMessage); } diff --git a/Luski.net/MainServer.Incoming.cs b/Luski.net/MainServer.Incoming.cs index 7d769e6..db82b06 100644 --- a/Luski.net/MainServer.Incoming.cs +++ b/Luski.net/MainServer.Incoming.cs @@ -1,4 +1,5 @@ using System; +using System.Text; using System.Text.Json; using System.Threading; using JacobTechEncryption; @@ -46,7 +47,8 @@ public partial class MainServer MainSocketMessage? m = JsonSerializer.Deserialize(e.Data); if (m is not null) { - m.decrypt(ClientEncryption.File.Channels.GetKey(m.ChannelID), CancellationToken.None); + m.decrypt(Storage.GetResourceKey(StorageDirectory.ChannelKeys, m.ChannelID.ToString(), + EncryptionHandler.Hash), CancellationToken.None); _ = MessageReceived.Invoke(m); } } @@ -94,8 +96,19 @@ public partial class MainServer { KeyExchange? KE = JsonSerializer.Deserialize(e.Data); if (KE is not null) - ClientEncryption.File.Channels.AddKey(KE.channel, - ClientEncryption.Encoder.GetString(Encryption.RSA.Decrypt(Convert.FromBase64String(KE.key), ClientEncryption.ofkey))); + { + Storage.SetResourceKey( + StorageDirectory.ChannelKeys, + KE.channel.ToString(), + EncryptionHandler.Hash, + Encoding.UTF8.GetString( + Encryption.RSA.Decrypt( + Convert.FromBase64String(KE.key), + EncryptionHandler.myPrivateKey + ) + ) + ); + } } catch (Exception ex) { diff --git a/Luski.net/MainServer.cs b/Luski.net/MainServer.cs index 1b23222..2076b42 100644 --- a/Luski.net/MainServer.cs +++ b/Luski.net/MainServer.cs @@ -5,6 +5,7 @@ using System.Text.Json.Serialization.Metadata; using System.Threading; using System.Threading.Tasks; using Luski.net.Enums; +using Luski.net.Enums.Main; using Luski.net.Interfaces; using Luski.net.JsonTypes.BaseTypes; using Luski.net.JsonTypes.HTTP; @@ -67,6 +68,8 @@ public partial class MainServer : Server chans.Remove(p); } } + + request.Server = this; chans.Add(request); return request; } @@ -88,6 +91,9 @@ public partial class MainServer : Server case MainSocketAppUser: user = (GetUser(UserID, MainSocketAppUserContext.Default.MainSocketAppUser, CancellationToken).Result as Tuser)!; break; + case MainSocketRemoteUser: + user = (GetUser(UserID, MainSocketRemoteUserContext.Default.MainSocketRemoteUser, CancellationToken).Result as Tuser)!; + break; case MainSocketUserBase: user = (GetUser(UserID, MainSocketUserBaseContext.Default.MainSocketUserBase, CancellationToken).Result as Tuser)!; break; @@ -110,11 +116,16 @@ public partial class MainServer : Server message = await GetFromServer("socketmessage", MainSocketMessageContext.Default.MainSocketMessage, CancellationToken, - new System.Collections.Generic.KeyValuePair("msg_id", id.ToString())); + new KeyValuePair("msg_id", id.ToString())); break; } } - if (message is not null) return message; + + if (message is not null) + { + message.Server = this; + return message; + } throw new Exception("Server did not return a message"); } @@ -143,6 +154,16 @@ public partial class MainServer : Server if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) { Tuser temp = poeople.Where(s => s is Tuser && s.Id == UserId).Cast().FirstOrDefault()!; + if (temp is MainSocketRemoteUser && (temp as MainSocketRemoteUser)!.Channel == null) + { + foreach (MainSocketDMChannel chan in chans.Where(s => s is MainSocketDMChannel).Cast()) + { + if (chan.Type == ChannelType.DM && chan.Id != 0 && chan.MemberIdList is not null) + { + if (chan.MemberIdList.Any(s => s == UserId)) (temp as MainSocketRemoteUser)!.Channel = chan; + } + } + } return temp; } while (true) @@ -165,6 +186,18 @@ public partial class MainServer : Server poeople.Remove(p); } } + + user.Server = this; + if (user is MainSocketRemoteUser && (user as MainSocketRemoteUser)!.Channel == null) + { + foreach (MainSocketDMChannel chan in chans.Where(s => s is MainSocketDMChannel).Cast()) + { + if (chan.Type == ChannelType.DM && chan.Id != 0 && chan.MemberIdList is not null) + { + if (chan.MemberIdList.Any(s => s == UserId)) (user as MainSocketRemoteUser)!.Channel = chan; + } + } + } poeople.Add(user); return user; } diff --git a/Luski.net/Server.Globals.cs b/Luski.net/Server.Globals.cs index 1a81f79..120898e 100644 --- a/Luski.net/Server.Globals.cs +++ b/Luski.net/Server.Globals.cs @@ -15,50 +15,10 @@ public partial class Server { public ServerType ServerType { get; internal set; } = ServerType.Public; public string Domain { get; internal set; } = default!; - public ServerCacheMode CacheMode { get; set; } = ServerCacheMode.None; public string ApiVersion { get; internal set; } = "v1"; internal WebSocket? ServerOut; internal string? Token = null, Error = null, gen = null; internal bool CanRequest = false, login = false; internal List poeople = new(); - public long UserID { get; internal set; } = 0; internal List chans { get; set; } = new(); - public string Cache - { - get - { - if (gen is null) - { - if (!Directory.Exists(JT)) Directory.CreateDirectory(JT); - string path = JT + "/Luski/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += "Data/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += UserID + "/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += "Cache/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += Path.GetRandomFileName() + "/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - gen = path; - } - if (!Directory.Exists($"{gen}/avatars")) Directory.CreateDirectory($"{gen}/avatars"); - if (!Directory.Exists($"{gen}/channels")) Directory.CreateDirectory($"{gen}/channels"); - return gen; - } - } - - internal static string JT - { - get - { - string tmp = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "JacobTech"); - if (OperatingSystem.IsLinux()) - { - tmp = Path.Combine(Environment.GetEnvironmentVariable("HOME")!, ".config/"); - tmp += "JacobTech"; - } - return tmp; - } - } } \ No newline at end of file diff --git a/Luski.net/Server.cs b/Luski.net/Server.cs index 6420dbd..7dfbbde 100644 --- a/Luski.net/Server.cs +++ b/Luski.net/Server.cs @@ -10,12 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Luski.net.Enums; using Luski.net.Interfaces; -using Luski.net.JsonTypes; using Luski.net.JsonTypes.BaseTypes; -using Luski.net.JsonTypes.HTTP; -using Luski.net.Structures; -using Luski.net.Structures.Main; -using Luski.net.Structures.Public; using File = System.IO.File; namespace Luski.net; @@ -23,16 +18,17 @@ namespace Luski.net; public partial class Server { internal Server() - { } + { + } + + public ServerEncryption EncryptionHandler { get; internal set; } = null!; + public ServerStorage Storage { get; internal set; } = null!; public async Task GetAvatar(CancellationToken CancellationToken) { - if (Cache != null) - { - bool isc = File.Exists($"{Cache}/servers/{Domain}"); - if (!isc) await GetFromServer($"socketserver/Avatar/", $"{Cache}/servers/{Domain}-{ApiVersion}", CancellationToken); - } - return File.ReadAllBytes($"{Cache}/servers/{Domain}"); + bool isc = File.Exists(Storage.GetStorageDirectory(StorageDirectory.ServerAssets) + "Icon"); + if (!isc) await GetFromServer($"socketserver/Avatar/", Storage.GetStorageDirectory(StorageDirectory.ServerAssets) + "Icon", CancellationToken); + return Storage.GetResourceBytes(StorageDirectory.ServerAssets, "Icon"); } public void SendServer(Tvalue Payload, JsonTypeInfo jsonTypeInfo) where Tvalue : IncomingWSS diff --git a/Luski.net/ServerEncryption.cs b/Luski.net/ServerEncryption.cs new file mode 100644 index 0000000..ef7b8e2 --- /dev/null +++ b/Luski.net/ServerEncryption.cs @@ -0,0 +1,56 @@ +using System; +using System.Security.Cryptography; +using JacobTechEncryption; + +namespace Luski.net; + +public class ServerEncryption +{ + internal bool Generating, Generated; + internal string ServerPublicKey = "", MyPublicKey = "", myPrivateKey = "", OfflinePrivateKey = "", OfflinePublicKey = ""; + internal byte[] Hash = default!; + internal ServerEncryption(string Domain) + { + //TODO Get server p key + } + + internal int PasswordVersion = 0; + internal byte[] LocalPasswordEncrypt(byte[] Password) => LocalPasswordEncrypt(Password, PasswordVersion); + internal string RemotePasswordEncrypt(byte[] Password) => RemotePasswordEncrypt(Password, PasswordVersion); + + internal byte[] LocalPasswordEncrypt(byte[] Password, int PasswordVersion) + { + return PasswordVersion switch + { + 0 => SHA256.Create().ComputeHash(Password), + _ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)), + }; + } + + internal string RemotePasswordEncrypt(byte[] Password, int PasswordVersion) + { + return PasswordVersion switch + { + 0 => Convert.ToBase64String(Encryption.RSA.Encrypt(LocalPasswordEncrypt(Password, PasswordVersion), ServerPublicKey)), + _ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)), + }; + } + + public void GenerateKeys() + { + if (!Generating) + { + Generating = true; + GenerateNewKeys(out MyPublicKey, out myPrivateKey); + GenerateNewKeys(out OfflinePublicKey, out OfflinePrivateKey); + Generated = true; + } + } + + public static void GenerateNewKeys(out string Public, out string Private, int KeySize = 4096) + { + using RSACryptoServiceProvider r = new(KeySize); + Private = r.ToXmlString(true); + Public = r.ToXmlString(false); + } +} \ No newline at end of file diff --git a/Luski.net/ServerStorage.cs b/Luski.net/ServerStorage.cs new file mode 100644 index 0000000..f492e36 --- /dev/null +++ b/Luski.net/ServerStorage.cs @@ -0,0 +1,116 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using JacobTechEncryption; +using Luski.net.Enums; +using Luski.net.JsonTypes; + +namespace Luski.net; + +public class ServerStorage +{ + public static readonly string[] Directories = new[] + { + "Info", + "Assets", + "Channels/Keys", + "Keys", + "Avatars", + "Channels/Icons", + "Channels/Messages" + }; + + private static readonly int[] CantDelete = new[] + { + (int)StorageDirectory.ChannelKeys, + (int)StorageDirectory.ServerKeys + }; + + internal ServerStorage(string Domain) + { + if (!Directory.Exists(JT)) Directory.CreateDirectory(JT); + Location = JT + "/Luski/"; + if (!Directory.Exists(Location)) Directory.CreateDirectory(Location); + Location += "Storage/"; + if (!Directory.Exists(Location)) Directory.CreateDirectory(Location); + Location += "Servers/"; + if (!Directory.Exists(Location)) Directory.CreateDirectory(Location); + Location += Domain+ "/"; + if (!Directory.Exists(Location)) Directory.CreateDirectory(Location); + if (!File.Exists(Location + "storage.json")) File.WriteAllText(Location + "storage.json", JsonSerializer.Serialize(new ServerStorageInfo(),ServerStorageInfoContext.Default.ServerStorageInfo)); + ServerStorageInfo info = JsonSerializer.Deserialize(File.ReadAllText(Location + "storage.json"), + ServerStorageInfoContext.Default.ServerStorageInfo)!; + CacheMode = info.CacheMode; + for (int i = 0; i < Directories.Length; i++) + { + string full = Location; + string[] spl = Directories[i].Split('/'); + if (!info.DontDelete && !CantDelete.Contains(i)) + { + try + { + if (Directory.Exists(full + spl[0])) Directory.Delete(full + spl[0], true); + } + catch + { + // ignored + } + } + + foreach (string d in spl) + { + full += d + "/"; + if (!Directory.Exists(full)) Directory.CreateDirectory(full); + } + } + } + + public string Location { get; internal set; } + + public string GetStorageDirectory(StorageDirectory Directory) + { + return Location + Directories[(byte)Directory] + "/"; + } + + public string GetResourceKey(StorageDirectory Directory, string Resource, string Key) + { + return Encoding.UTF8.GetString(Encryption.AES.Decrypt(GetResourceBytes(Directory, Resource), Key)); + } + + public string GetResourceKey(StorageDirectory Directory, string Resource, byte[] Key) + { + return Encoding.UTF8.GetString(Encryption.AES.Decrypt(GetResourceBytes(Directory, Resource), Encoding.UTF8.GetString(Key))); + } + + public void SetResourceKey(StorageDirectory Directory, string Resource, string Key, string value) + { + File.WriteAllBytes(GetStorageDirectory(Directory) + Resource, Encryption.AES.Encrypt(Encoding.UTF8.GetBytes(value), Key)); + } + public void SetResourceKey(StorageDirectory Directory, string Resource, byte[] Key, string value) + { + File.WriteAllBytes(GetStorageDirectory(Directory) + Resource, Encryption.AES.Encrypt(Encoding.UTF8.GetBytes(value), Encoding.UTF8.GetString(Key))); + } + + public byte[] GetResourceBytes(StorageDirectory Directory, string Resource) + { + return File.ReadAllBytes(Location + Directories[(byte)Directory] + "/" + Resource); + } + + public CacheMode CacheMode { get; internal set; } + + internal static string JT + { + get + { + string tmp = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "JacobTech"); + if (OperatingSystem.IsLinux()) + { + tmp = Path.Combine(Environment.GetEnvironmentVariable("HOME")!, ".config/"); + tmp += "JacobTech"; + } + return tmp; + } + } +} \ No newline at end of file diff --git a/Luski.net/Structures/File.cs b/Luski.net/Structures/File.cs index 8c3e76f..d012a44 100755 --- a/Luski.net/Structures/File.cs +++ b/Luski.net/Structures/File.cs @@ -18,7 +18,7 @@ namespace Luski.net.Structures; public class File : IncomingHTTP { - public IServer Server { get; internal set; } + public MainServer Server { get; internal set; } [JsonInclude] [JsonPropertyName("name")] public string Name { get; internal set; } = default!; @@ -35,6 +35,8 @@ public class File : IncomingHTTP public async Task DownloadBytes(string Loc, long key, CancellationToken CancellationToken) { + //TODO make better + //using HttpClient web = new(); //web.DefaultRequestHeaders.Add("token", Server.Token); //web.DefaultRequestHeaders.Add("id", id.ToString()); @@ -42,7 +44,7 @@ public class File : IncomingHTTP string path = Path.GetTempFileName(); await Server.GetFromServer($"SocketMessage/GetFile/{Id}", path, CancellationToken); - string Key = (key == 0 ? ClientEncryption.MyPublicKey : ClientEncryption.File.Channels.GetKey(key))!; + string Key = (key == 0 ? Server.EncryptionHandler.MyPublicKey : "") ;// ClientEncryption.File.Channels.GetKey(key))!; Encryption.AES.DecryptToFile(System.IO.File.ReadAllBytes(path), Key, Loc); /* if (request is not null && request.Error is not null) diff --git a/Luski.net/Structures/Main/MainSocketChannel.cs b/Luski.net/Structures/Main/MainSocketChannel.cs index 5d5bdd2..050c19c 100755 --- a/Luski.net/Structures/Main/MainSocketChannel.cs +++ b/Luski.net/Structures/Main/MainSocketChannel.cs @@ -70,7 +70,8 @@ public class MainSocketChannel : IncomingHTTP } int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 50) * 2.0)); if (num == 0) num = 1; - string? lkey = ClientEncryption.File.Channels.GetKey(Id); + string lkey = Server.Storage.GetResourceKey(StorageDirectory.ChannelKeys, Id.ToString(), + Server.EncryptionHandler.Hash); Parallel.ForEach(Members, new ParallelOptions() { MaxDegreeOfParallelism = num @@ -95,7 +96,9 @@ public class MainSocketChannel : IncomingHTTP } internal Task StartKeyProcessAsync(CancellationToken CancellationToken) - {/* + { + //TODO code key exchange + /* ClientEncryption.GenerateNewKeys(out string Public, out string Private); Key = Public; HttpResponseMessage b; diff --git a/Luski.net/Structures/Main/MainSocketRemoteUser.cs b/Luski.net/Structures/Main/MainSocketRemoteUser.cs index 34ab431..83bdd63 100755 --- a/Luski.net/Structures/Main/MainSocketRemoteUser.cs +++ b/Luski.net/Structures/Main/MainSocketRemoteUser.cs @@ -18,6 +18,7 @@ public class MainSocketRemoteUser : MainSocketUserBase [JsonPropertyName("friend_status")] [JsonInclude] public FriendStatus FriendStatus { get; internal set; } = default!; + public MainSocketDMChannel Channel { get; internal set; } internal MainSocketRemoteUser Clone() { diff --git a/Luski.net/Structures/Main/MainSocketTextChannel.cs b/Luski.net/Structures/Main/MainSocketTextChannel.cs index f07062a..3147f0c 100755 --- a/Luski.net/Structures/Main/MainSocketTextChannel.cs +++ b/Luski.net/Structures/Main/MainSocketTextChannel.cs @@ -27,12 +27,9 @@ public class MainSocketTextChannel : MainSocketChannel if (Type == ChannelType.DM) return Members.First().GetAvatar(CancellationToken).Result; else { - if (Server.Cache != null) - { - bool isc = System.IO.File.Exists($"{Server.Cache}/channels/{Id}"); - if (!isc) await Server.GetFromServer($"SocketChannel/GetPicture/{Id}", $"{Server.Cache}/channels/{Id}", CancellationToken); - } - return System.IO.File.ReadAllBytes($"{Server.Cache}/channels/{Id}"); + bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + Id.ToString()); + if (!isc) await Server.GetFromServer($"SocketChannel/GetPicture/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + Id.ToString(), CancellationToken); + return Server.Storage.GetResourceBytes(StorageDirectory.ChannelIcons, Id.ToString()); } } @@ -59,7 +56,8 @@ public class MainSocketTextChannel : MainSocketChannel int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 5) * 2.0)); if (num == 0) num = 1; - string? key = ClientEncryption.File.Channels.GetKey(Id); + string key = Server.Storage.GetResourceKey(StorageDirectory.ChannelKeys, Id.ToString(), + Server.EncryptionHandler.Hash); if (data is null) throw new Exception("Invalid data from server"); if (data.Messages is null) data.Messages = Array.Empty(); Parallel.ForEach(data.Messages, new ParallelOptions() @@ -105,7 +103,8 @@ public class MainSocketTextChannel : MainSocketChannel { int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 5) * 2.0)); if (num == 0) num = 1; - string? key = ClientEncryption.File.Channels.GetKey(Id); + string key = Server.Storage.GetResourceKey(StorageDirectory.ChannelKeys, Id.ToString(), + Server.EncryptionHandler.Hash); if (data.Messages is null) data.Messages = Array.Empty(); Parallel.ForEach(data.Messages, new ParallelOptions() { @@ -140,8 +139,8 @@ public class MainSocketTextChannel : MainSocketChannel public async Task SendMessage(string Message, CancellationToken CancellationToken, params File?[] Files) { - string key = ClientEncryption.File.Channels.GetKey(Id); - if (Id == 0) key = "";//ClientEncryption.ServerPublicKey; + string key = Server.Storage.GetResourceKey(StorageDirectory.ChannelKeys, Id.ToString(), + Server.EncryptionHandler.Hash); JsonTypes.HTTP.Message m = new() { Context = Convert.ToBase64String(Encryption.RSA.Encrypt(Message, key, EncoderType.UTF8)), diff --git a/Luski.net/Structures/Main/MainSocketUserBase.cs b/Luski.net/Structures/Main/MainSocketUserBase.cs index 7b63322..35c9ded 100644 --- a/Luski.net/Structures/Main/MainSocketUserBase.cs +++ b/Luski.net/Structures/Main/MainSocketUserBase.cs @@ -24,12 +24,9 @@ public class MainSocketUserBase : IncomingHTTP, IUser public PictureType PictureType { get; internal set; } = default!; public async Task GetAvatar(CancellationToken CancellationToken) { - if (Server.Cache != null) - { - bool isc = System.IO.File.Exists($"{Server.Cache}/avatars/{Id}"); - if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", $"{Server.Cache}/avatars/{Id}", CancellationToken); - } - return System.IO.File.ReadAllBytes($"{Server.Cache}/avatars/{Id}"); + bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString()); + if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString(), CancellationToken); + return Server.Storage.GetResourceBytes(StorageDirectory.Avatars, Id.ToString()); } public Task GetUserKey(CancellationToken CancellationToken) diff --git a/Luski.net/Structures/Public/MainSocketUserBase.cs b/Luski.net/Structures/Public/PublicSocketUserBase.cs similarity index 79% rename from Luski.net/Structures/Public/MainSocketUserBase.cs rename to Luski.net/Structures/Public/PublicSocketUserBase.cs index 0252a20..4fab6c2 100644 --- a/Luski.net/Structures/Public/MainSocketUserBase.cs +++ b/Luski.net/Structures/Public/PublicSocketUserBase.cs @@ -24,12 +24,9 @@ public class PublicSocketUserBase : IncomingHTTP, IUser public PictureType PictureType { get; internal set; } = default!; public async Task GetAvatar(CancellationToken CancellationToken) { - if (Server.Cache != null) - { - bool isc = System.IO.File.Exists($"{Server.Cache}/avatars/{Id}"); - if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", $"{Server.Cache}/avatars/{Id}", CancellationToken); - } - return System.IO.File.ReadAllBytes($"{Server.Cache}/avatars/{Id}"); + bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString()); + if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString(), CancellationToken); + return Server.Storage.GetResourceBytes(StorageDirectory.Avatars, Id.ToString()); } public Task GetUserKey(CancellationToken CancellationToken)