commit 9961b7792c765aa284e735a9bcd962e931780864 Author: WeeXnes Date: Tue May 6 02:18:04 2025 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..add57be --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ \ No newline at end of file diff --git a/Cryptura.sln b/Cryptura.sln new file mode 100644 index 0000000..4ef278a --- /dev/null +++ b/Cryptura.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cryptura", "Cryptura\Cryptura.csproj", "{7C536079-3ADF-4E9B-9A0F-DC0DC743DF60}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7C536079-3ADF-4E9B-9A0F-DC0DC743DF60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C536079-3ADF-4E9B-9A0F-DC0DC743DF60}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C536079-3ADF-4E9B-9A0F-DC0DC743DF60}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C536079-3ADF-4E9B-9A0F-DC0DC743DF60}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Cryptura/App.axaml b/Cryptura/App.axaml new file mode 100644 index 0000000..55cf8c5 --- /dev/null +++ b/Cryptura/App.axaml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/Cryptura/App.axaml.cs b/Cryptura/App.axaml.cs new file mode 100644 index 0000000..67dee72 --- /dev/null +++ b/Cryptura/App.axaml.cs @@ -0,0 +1,23 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; + +namespace Cryptura; + +public partial class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow(); + } + + base.OnFrameworkInitializationCompleted(); + } +} \ No newline at end of file diff --git a/Cryptura/Core.cs b/Cryptura/Core.cs new file mode 100644 index 0000000..11008ed --- /dev/null +++ b/Cryptura/Core.cs @@ -0,0 +1,20 @@ +using System; +using System.IO; +using Avalonia.Controls; + +namespace Cryptura; + +public class Core +{ + public static bool IsRunningOnGnome() + { + var xdgDesktop = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP"); + return xdgDesktop != null && xdgDesktop.Contains("GNOME", StringComparison.OrdinalIgnoreCase); + } + + public static void SetClipboardText(string text, Window window) + { + window.Clipboard?.SetTextAsync(text); + } +} + diff --git a/Cryptura/Cryptura.csproj b/Cryptura/Cryptura.csproj new file mode 100644 index 0000000..9ccc65a --- /dev/null +++ b/Cryptura/Cryptura.csproj @@ -0,0 +1,22 @@ + + + WinExe + net9.0 + enable + true + app.manifest + true + + + + + + + + + + None + All + + + diff --git a/Cryptura/EncryptionLibrary.cs b/Cryptura/EncryptionLibrary.cs new file mode 100644 index 0000000..7e673fb --- /dev/null +++ b/Cryptura/EncryptionLibrary.cs @@ -0,0 +1,64 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace Cryptura; + +public static class EncryptionLibrary +{ + private const int SaltSize = 16; + private const int KeySize = 32; + private const int Iterations = 10000; + + public static string Encrypt(string masterPassword, string plainPassword) + { + byte[] salt = new byte[SaltSize]; + using (var rng = new RNGCryptoServiceProvider()) + { + rng.GetBytes(salt); + } + + using (var aesAlg = Aes.Create()) + { + var key = new Rfc2898DeriveBytes(masterPassword, salt, Iterations); + aesAlg.Key = key.GetBytes(KeySize); + aesAlg.IV = new byte[16]; + + using (var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV)) + using (var ms = new MemoryStream()) + { + ms.Write(salt, 0, salt.Length); + + using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) + using (var writer = new StreamWriter(cs)) + { + writer.Write(plainPassword); + } + + return Convert.ToBase64String(ms.ToArray()); + } + } + } + public static string Decrypt(string masterPassword, string encryptedPassword) + { + byte[] cipherBytes = Convert.FromBase64String(encryptedPassword); + byte[] salt = new byte[SaltSize]; + Array.Copy(cipherBytes, 0, salt, 0, SaltSize); // Extract the salt + + using (var aesAlg = Aes.Create()) + { + var key = new Rfc2898DeriveBytes(masterPassword, salt, Iterations); + aesAlg.Key = key.GetBytes(KeySize); + aesAlg.IV = new byte[16]; + + using (var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV)) + using (var ms = new MemoryStream(cipherBytes, SaltSize, cipherBytes.Length - SaltSize)) + using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) + using (var reader = new StreamReader(cs)) + { + return reader.ReadToEnd(); + } + } + } +} \ No newline at end of file diff --git a/Cryptura/Globals.cs b/Cryptura/Globals.cs new file mode 100644 index 0000000..d437854 --- /dev/null +++ b/Cryptura/Globals.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; +using Microsoft.VisualBasic.CompilerServices; + +namespace Cryptura; + +public class Globals +{ + public static string GetKeyDirectory() + { + string homeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + string keyDirectory = Path.Combine(homeDir, ".cryptura"); + Util.CheckDir(keyDirectory); + return keyDirectory; + } + + public static string GenerateUuidFilename() + { + return Guid.NewGuid() + ".wx"; + } +} \ No newline at end of file diff --git a/Cryptura/MainWindow.axaml b/Cryptura/MainWindow.axaml new file mode 100644 index 0000000..4c29f3d --- /dev/null +++ b/Cryptura/MainWindow.axaml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cryptura/MainWindow.axaml.cs b/Cryptura/MainWindow.axaml.cs new file mode 100644 index 0000000..ff7b710 --- /dev/null +++ b/Cryptura/MainWindow.axaml.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Interactivity; +using Avalonia.Media; + +namespace Cryptura; + +public partial class MainWindow : Window +{ + public static bool censorPasswords = true; + private void AdjustThemeToPlatform() + { + if (Core.IsRunningOnGnome()) + { + this.TransparencyLevelHint = new[] { WindowTransparencyLevel.None }; + this.Background = new SolidColorBrush(Color.Parse("#201c29")); + AcrylicBorderObject.IsVisible = false; + } + else + { + this.TransparencyLevelHint = new[] { WindowTransparencyLevel.AcrylicBlur }; + this.Background = Brushes.Transparent; + AcrylicBorderObject.IsVisible = true; + } + } + public MainWindow() + { + InitializeComponent(); + AdjustThemeToPlatform(); + FetchPasswords(); + Password pw = new Password("monkeyman", "Himself"); + } + + private void FetchPasswords(){ + List passwords = new List(); + string[] WXFiles = Directory.GetFiles(Globals.GetKeyDirectory()); + foreach (string file in WXFiles) + { + if (Path.GetExtension(file).ToLower() == ".kf") + { + Password pw = new Password(file); + pw.WxFile.Verify(); + } + else + { + Console.WriteLine("Skipped non-key file: " + file); + } + + } + PasswordList.ItemsSource = passwords; + } + + private void Control_OnLoaded(object? sender, RoutedEventArgs e) + { + if (sender is TextBox textBox) + { + Console.WriteLine("called"); + textBox.Bind(TextBlock.TextProperty, new Binding("Value")); + } + } + + private void Button_OnClick(object? sender, RoutedEventArgs e) + { + censorPasswords = !censorPasswords; + FetchPasswords(); + } + + private void PasswordList_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) + { + if (PasswordList.SelectedItem is Password password) Core.SetClipboardText(password.Value, this); + } +} \ No newline at end of file diff --git a/Cryptura/Password.cs b/Cryptura/Password.cs new file mode 100644 index 0000000..8723de0 --- /dev/null +++ b/Cryptura/Password.cs @@ -0,0 +1,36 @@ +using System; +using System.IO; +using System.Net; +using Avalonia.Data.Core; + +namespace Cryptura; + +public class Password +{ + public string Name { get; set; } + public string Value { get; set; } + public string CensoredValue { get; set; } = "******"; + public WXFile WxFile { get; set; } + + public string DisplayValue + { + get => MainWindow.censorPasswords ? Value : CensoredValue; + } + + public Password(string name, string value) + { + this.Name = name; + this.Value = value; + this.WxFile = new WXFile(Path.Combine(Globals.GetKeyDirectory(), Globals.GenerateUuidFilename())); + this.WxFile.WriteHeader(); + this.WxFile.SetName(this.Name); + this.WxFile.SetValue(this.Value); + } + + public Password(string filepath) + { + + } + + +} \ No newline at end of file diff --git a/Cryptura/Program.cs b/Cryptura/Program.cs new file mode 100644 index 0000000..b31a0de --- /dev/null +++ b/Cryptura/Program.cs @@ -0,0 +1,21 @@ +using Avalonia; +using System; + +namespace Cryptura; + +class Program +{ + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + [STAThread] + public static void Main(string[] args) => BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .WithInterFont() + .LogToTrace(); +} \ No newline at end of file diff --git a/Cryptura/Util.cs b/Cryptura/Util.cs new file mode 100644 index 0000000..96dd2ca --- /dev/null +++ b/Cryptura/Util.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Avalonia.Controls; +using Avalonia.Media.Imaging; + +namespace Cryptura; + +public static class Util +{ + public static string? OpenFileDialogSync(Window parent) + { + var dialog = new OpenFileDialog + { + Title = "Select PS2 ISO", + AllowMultiple = false, + Filters = new List + { + new FileDialogFilter { Name = "PS2 ISO", Extensions = { "iso", "bin" } }, + new FileDialogFilter { Name = "All Files", Extensions = { "*" } } + } + }; + + var result = dialog.ShowAsync(parent).GetAwaiter().GetResult(); + return result?.FirstOrDefault(); + } + + public static void CheckDir(string path) + { + if (!Directory.Exists(path)) + { + Console.WriteLine("Directory " + path + " wasn't found, creating now."); + Directory.CreateDirectory(path); + } + else + { + Console.WriteLine("Directory was found: " + path); + } + } +} diff --git a/Cryptura/WXFile.cs b/Cryptura/WXFile.cs new file mode 100644 index 0000000..9a5fa30 --- /dev/null +++ b/Cryptura/WXFile.cs @@ -0,0 +1,120 @@ +using System; +using System.IO; + +namespace Cryptura +{ + public class WXFile + { + public string Path { get; set; } + + public WXFile(string path) + { + this.Path = path; + } + + public bool Verify() + { + try + { + var lines = File.ReadAllLines(this.Path); + + if (lines.Length < 3) + return false; + + if (lines[0] != "##WXFile##") + return false; + + if (string.IsNullOrWhiteSpace(lines[1]) || string.IsNullOrWhiteSpace(lines[2])) + return false; + + return true; + } + catch (Exception ex) + { + Console.WriteLine($"Error reading file: {ex.Message}"); + return false; + } + } + + public string GetName() + { + try + { + var lines = File.ReadAllLines(this.Path); + return lines.Length >= 2 ? lines[1] : ""; + } + catch + { + return ""; + } + } + + public void SetName(string value) + { + try + { + var lines = File.ReadAllLines(this.Path); + + if (lines.Length < 3) + Array.Resize(ref lines, 3); + + lines[1] = value; + File.WriteAllLines(this.Path, lines); + } + catch (Exception ex) + { + Console.WriteLine($"Error writing name: {ex.Message}"); + } + } + + public string GetValue() + { + try + { + var lines = File.ReadAllLines(this.Path); + return lines.Length >= 3 ? lines[2] : ""; + } + catch + { + return ""; + } + } + + public void SetValue(string value) + { + try + { + var lines = File.ReadAllLines(this.Path); + + if (lines.Length < 3) + Array.Resize(ref lines, 3); + + lines[2] = value; + File.WriteAllLines(this.Path, lines); + } + catch (Exception ex) + { + Console.WriteLine($"Error writing value: {ex.Message}"); + } + } + + public void WriteHeader() + { + try + { + var lines = File.Exists(this.Path) ? File.ReadAllLines(this.Path) : new string[0]; + + if (lines.Length < 1) + Array.Resize(ref lines, 1); + + lines[0] = "##WXFile##"; + + File.WriteAllLines(this.Path, lines); + } + catch (Exception ex) + { + Console.WriteLine($"Error writing header: {ex.Message}"); + } + } + } +} diff --git a/Cryptura/app.manifest b/Cryptura/app.manifest new file mode 100644 index 0000000..3a7e58b --- /dev/null +++ b/Cryptura/app.manifest @@ -0,0 +1,18 @@ + + + + + + + + + + + + + +