From 81f266d8c4dc29fd613e05f3b83dd6cb4654e3db Mon Sep 17 00:00:00 2001 From: TheXamlGuy Date: Tue, 30 Apr 2024 20:46:47 +0100 Subject: [PATCH] wip --- Bitvault.Avalonia/App.axaml.cs | 14 +-- Bitvault/AesDecryptor.cs | 27 +++--- Bitvault/AesEncryptor.cs | 33 ++++--- Bitvault/CreateVaultHandler.cs | 25 ++--- Bitvault/CreateVaultStorageHandler.cs | 134 ++++++++++++++------------ Bitvault/IDecryptor.cs | 10 +- Bitvault/IEncryptor.cs | 11 +-- Bitvault/IKeyGenerator.cs | 6 ++ Bitvault/IVaultComponentFactory.cs | 9 ++ Bitvault/IVaultFactory.cs | 7 ++ Bitvault/IVaultKeyGenerator.cs | 6 ++ Bitvault/IVaultStorage.cs | 6 ++ Bitvault/KeyGenerator.cs | 14 +++ Bitvault/OpenVaultHandler.cs | 8 +- Bitvault/OpenVaultStorageHandler.cs | 70 +++++++------- Bitvault/VaultComponentFactory.cs | 18 ++++ Bitvault/VaultConfiguration.cs | 2 + Bitvault/VaultFactory.cs | 13 +++ Bitvault/VaultKey.cs | 3 + Bitvault/VaultKeyGenerator.cs | 21 ++++ Bitvault/VaultStorage.cs | 19 +++- Toolkit | 2 +- 22 files changed, 296 insertions(+), 162 deletions(-) create mode 100644 Bitvault/IKeyGenerator.cs create mode 100644 Bitvault/IVaultComponentFactory.cs create mode 100644 Bitvault/IVaultFactory.cs create mode 100644 Bitvault/IVaultKeyGenerator.cs create mode 100644 Bitvault/IVaultStorage.cs create mode 100644 Bitvault/KeyGenerator.cs create mode 100644 Bitvault/VaultComponentFactory.cs create mode 100644 Bitvault/VaultFactory.cs create mode 100644 Bitvault/VaultKey.cs create mode 100644 Bitvault/VaultKeyGenerator.cs diff --git a/Bitvault.Avalonia/App.axaml.cs b/Bitvault.Avalonia/App.axaml.cs index e2b35be..d7c67d4 100644 --- a/Bitvault.Avalonia/App.axaml.cs +++ b/Bitvault.Avalonia/App.axaml.cs @@ -54,12 +54,17 @@ public partial class App : Application { args.AddServices(services => { + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); - + + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddDbContextFactory(args => { args.UseSqlite(); @@ -69,9 +74,6 @@ public partial class App : Application services.AddHandler(); - services.AddHandler(); - services.AddHandler(); - services.AddTemplate(); services.AddTemplate(); services.AddTemplate(); @@ -83,13 +85,13 @@ public partial class App : Application }); })!); - services.AddSingleton(); + services.AddTransient(); services.AddHandler(); + services.AddSingleton(); services.AddInitializer(); services.AddTemplate("Main"); - services.AddHandler(); services.AddTransient(); diff --git a/Bitvault/AesDecryptor.cs b/Bitvault/AesDecryptor.cs index 76db061..6e4d359 100644 --- a/Bitvault/AesDecryptor.cs +++ b/Bitvault/AesDecryptor.cs @@ -7,23 +7,22 @@ public class AesDecryptor : { private const int IvSize = 16; - public string Decrypt(string cipherText, byte[] key) + public byte[] Decrypt(byte[] cipher, byte[] key) { - byte[] cipherData = Convert.FromBase64String(cipherText); + Span iv = cipher.AsSpan(0, IvSize); + ReadOnlySpan encryptedContent = cipher.AsSpan(IvSize); - byte[] iv = new byte[IvSize]; - Array.Copy(cipherData, 0, iv, 0, IvSize); // Extract the IV from the start of the cipher data - - using var aes = Aes.Create(); + using Aes aes = Aes.Create(); aes.Key = key; - aes.IV = iv; + aes.IV = iv.ToArray(); - using var memoryStream = new MemoryStream(cipherData, IvSize, cipherData.Length - IvSize); - using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV)) - using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) - using (var streamReader = new StreamReader(cryptoStream)) - { - return streamReader.ReadToEnd(); // Return the decrypted text - } + using MemoryStream memoryStream = new(encryptedContent.ToArray()); + using ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV); + using CryptoStream cryptoStream = new(memoryStream, decryptor, CryptoStreamMode.Read); + + using MemoryStream resultStream = new(); + cryptoStream.CopyTo(resultStream); + + return resultStream.ToArray(); } } diff --git a/Bitvault/AesEncryptor.cs b/Bitvault/AesEncryptor.cs index 6f1c168..fec9065 100644 --- a/Bitvault/AesEncryptor.cs +++ b/Bitvault/AesEncryptor.cs @@ -2,28 +2,33 @@ namespace Bitvault; -public class AesEncryptor : IEncryptor +public class AesEncryptor : + IEncryptor { - public string Encrypt(string plainText, byte[] key) - { - const int IvSize = 16; + private const int IvSize = 16; - using var aes = Aes.Create(); + public byte[] Encrypt(byte[] data, + byte[] key) + { + if (key.Length != 32) + { + throw new ArgumentException("Key must be 256 bits (32 bytes)."); + } + + using Aes aes = Aes.Create(); aes.Key = key; aes.GenerateIV(); - byte[] iv = aes.IV; + using MemoryStream memoryStream = new(); + memoryStream.Write(aes.IV.AsSpan(0, IvSize)); - using var memoryStream = new MemoryStream(); - memoryStream.Write(iv, 0, IvSize); // Store IV at the start of the stream - - using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV)) - using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) - using (var streamWriter = new StreamWriter(cryptoStream)) + using (ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV)) + using (CryptoStream cryptoStream = new(memoryStream, encryptor, CryptoStreamMode.Write)) { - streamWriter.Write(plainText); + cryptoStream.Write(data, 0, data.Length); + cryptoStream.FlushFinalBlock(); } - return Convert.ToBase64String(memoryStream.ToArray()); // Return the encrypted data in base64 + return memoryStream.ToArray(); } } diff --git a/Bitvault/CreateVaultHandler.cs b/Bitvault/CreateVaultHandler.cs index b5ba8e3..46cddb1 100644 --- a/Bitvault/CreateVaultHandler.cs +++ b/Bitvault/CreateVaultHandler.cs @@ -3,30 +3,23 @@ using Toolkit.Foundation; namespace Bitvault; -public class CreateVaultHandler(IComponentFactory componentFactory) : +public class CreateVaultHandler(IVaultComponentFactory vaultComponentFactory) : IHandler, bool> { - public async Task Handle(Create args, + public Task Handle(Create args, CancellationToken cancellationToken) { - if (args.Value is Vault vault) + if (args.Value is Vault vault && vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password) { - if (vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password) + if (vaultComponentFactory.Create(name) is IComponentHost host) { - if (componentFactory.Create($"Vault:{name}", new VaultConfiguration { Name = name }) is IComponentHost host) - { - if (host.Services.GetRequiredService() is IMediator mediator) - { - if (await mediator.Handle, bool>(Create.As(new VaultStorage(name, password)), cancellationToken)) - { - await host.StartAsync(cancellationToken); - return true; - } - } - } + IVaultFactory factory = host.Services.GetRequiredService(); + factory.Create(name, password); + + return Task.FromResult(true); } } - return false; + return Task.FromResult(false); } } \ No newline at end of file diff --git a/Bitvault/CreateVaultStorageHandler.cs b/Bitvault/CreateVaultStorageHandler.cs index 2ac39ea..1f2567b 100644 --- a/Bitvault/CreateVaultStorageHandler.cs +++ b/Bitvault/CreateVaultStorageHandler.cs @@ -1,73 +1,89 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Hosting; -using System.Security.Cryptography; -using Toolkit.Foundation; +//using Microsoft.EntityFrameworkCore; +//using Microsoft.Extensions.Hosting; +//using Toolkit.Foundation; -namespace Bitvault; +//namespace Bitvault; -public class CreateVaultStorageHandler(IHostEnvironment environment, - IKeyDeriver deriver, - IEncryptor encryptor, - IDecryptor decryptor, - IDbContextFactory dbContextFactory) : IHandler, bool> -{ - public async Task Handle(Create args, CancellationToken cancellationToken) - { - if (args.Value is VaultStorage vault) - { - if (vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password) - { - byte[] salt = new byte[16]; - RandomNumberGenerator.Fill(salt); +//public class CreateVaultStorageHandler(IHostEnvironment environment, +// IKeyGenerator generator, +// IKeyDeriver deriver, +// IEncryptor encryptor, +// IDecryptor decryptor, +// IDbContextFactory dbContextFactory, +// IWritableConfiguration writer) : IHandler, bool> +//{ +// public async Task Handle(Create args, CancellationToken cancellationToken) +// { +// if (args.Value is VaultStorage vault) +// { +// if (vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password) +// { +// byte[] salt = generator.Generate(16); +// byte[] key = generator.Generate(32); - byte[] key = new byte[32]; - RandomNumberGenerator.Fill(key); +// byte[] derivedKey = deriver.DeriveKey(password, salt); - byte[] derivedKey = deriver.DeriveKey(password, salt); - string? encryptedKey = encryptor.Encrypt(Convert.ToBase64String(key), derivedKey); +// byte[] encryptedKey = encryptor.Encrypt(key, derivedKey); +// byte[] decryptedKey = decryptor.Decrypt(encryptedKey, derivedKey); + +// Array.Clear(encryptedKey, 0, encryptedKey.Length); + +// using VaultDbContext context = dbContextFactory.CreateDbContext(); +// context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}.vault;Mode=ReadWriteCreate;Password={Convert.ToBase64String(decryptedKey)}"); + +// try +// { +// await context.Database.EnsureCreatedAsync(cancellationToken); +// writer.Write(args => +// { +// var f = args.Name; +// }); +// } +// catch +// { + +// } + +// Array.Clear(decryptedKey, 0, decryptedKey.Length); + +// // Derive the key for encryption + +// //byte[] encryptionKey = deriver.DeriveKey(password, salt); + +// //// Derive the key for decryption +// //byte[] decryptionKey = deriver.DeriveKey(password, salt); + +// //// Compare keys to ensure they're the same +// //bool areKeysEqual = encryptionKey.SequenceEqual(decryptionKey); + +// ////byte[] derivedKey = deriver.DeriveKey(password, salt); +// //string? encrypted = encryptor.Encrypt(password, derivedKey); + +// //var storedSalt = Convert.ToBase64String(salt); - byte[] derivedKey2 = deriver.DeriveKey(password, salt); - var dod = decryptor.Decrypt(encryptedKey, derivedKey2); +// //byte[] derivedKey2 = deriver.DeriveKey(password, salt); - // Derive the key for encryption +// //var d = decryptor.Decrypt(encrypted, derivedKey2); - //byte[] encryptionKey = deriver.DeriveKey(password, salt); +// // Generate a hash +// //string hash = hasher.HashPassword(password); - //// Derive the key for decryption - //byte[] decryptionKey = deriver.DeriveKey(password, salt); +// //string[] parts = hash.Split(':'); - //// Compare keys to ensure they're the same - //bool areKeysEqual = encryptionKey.SequenceEqual(decryptionKey); +// //// Store the salt only +// //string storedSalt = parts[0]; - ////byte[] derivedKey = deriver.DeriveKey(password, salt); - //string? encrypted = encryptor.Encrypt(password, derivedKey); +// //// Use the hash as the password +// //string storedHash = parts[1]; - //var storedSalt = Convert.ToBase64String(salt); +// //context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}.vault;Mode=ReadWriteCreate;Password={storedHash}"); +// //await context.Database.EnsureCreatedAsync(); +// return true; +// } +// } - //byte[] derivedKey2 = deriver.DeriveKey(password, salt); - - //var d = decryptor.Decrypt(encrypted, derivedKey2); - - // Generate a hash - //string hash = hasher.HashPassword(password); - - //string[] parts = hash.Split(':'); - - //// Store the salt only - //string storedSalt = parts[0]; - - //// Use the hash as the password - //string storedHash = parts[1]; - - //context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}.vault;Mode=ReadWriteCreate;Password={storedHash}"); - //await context.Database.EnsureCreatedAsync(); - - return true; - } - } - - return false; - } -} \ No newline at end of file +// return false; +// } +//} \ No newline at end of file diff --git a/Bitvault/IDecryptor.cs b/Bitvault/IDecryptor.cs index 8a59e8e..bfe8f27 100644 --- a/Bitvault/IDecryptor.cs +++ b/Bitvault/IDecryptor.cs @@ -1,7 +1,7 @@ -namespace Bitvault +namespace Bitvault; + +public interface IDecryptor { - public interface IDecryptor - { - string? Decrypt(string cipherText, byte[] key); - } + byte[] Decrypt(byte[] cipher, + byte[] key); } \ No newline at end of file diff --git a/Bitvault/IEncryptor.cs b/Bitvault/IEncryptor.cs index 5deea87..c438b36 100644 --- a/Bitvault/IEncryptor.cs +++ b/Bitvault/IEncryptor.cs @@ -1,8 +1,7 @@ -namespace Bitvault -{ - public interface IEncryptor - { +namespace Bitvault; - string? Encrypt(string plainText, byte[] key); - } +public interface IEncryptor +{ + + byte[] Encrypt(byte[] data, byte[] key); } \ No newline at end of file diff --git a/Bitvault/IKeyGenerator.cs b/Bitvault/IKeyGenerator.cs new file mode 100644 index 0000000..e6536a4 --- /dev/null +++ b/Bitvault/IKeyGenerator.cs @@ -0,0 +1,6 @@ +namespace Bitvault; + +public interface IKeyGenerator +{ + byte[] Generate(int size); +} \ No newline at end of file diff --git a/Bitvault/IVaultComponentFactory.cs b/Bitvault/IVaultComponentFactory.cs new file mode 100644 index 0000000..b02c55f --- /dev/null +++ b/Bitvault/IVaultComponentFactory.cs @@ -0,0 +1,9 @@ +using Toolkit.Foundation; + +namespace Bitvault +{ + public interface IVaultComponentFactory + { + IComponentHost? Create(string name); + } +} \ No newline at end of file diff --git a/Bitvault/IVaultFactory.cs b/Bitvault/IVaultFactory.cs new file mode 100644 index 0000000..931155b --- /dev/null +++ b/Bitvault/IVaultFactory.cs @@ -0,0 +1,7 @@ +namespace Bitvault +{ + public interface IVaultFactory + { + bool Create(string name, string password); + } +} \ No newline at end of file diff --git a/Bitvault/IVaultKeyGenerator.cs b/Bitvault/IVaultKeyGenerator.cs new file mode 100644 index 0000000..5f6521c --- /dev/null +++ b/Bitvault/IVaultKeyGenerator.cs @@ -0,0 +1,6 @@ +namespace Bitvault; + +public interface IVaultKeyGenerator +{ + VaultKey Create(string password); +} \ No newline at end of file diff --git a/Bitvault/IVaultStorage.cs b/Bitvault/IVaultStorage.cs new file mode 100644 index 0000000..1961e31 --- /dev/null +++ b/Bitvault/IVaultStorage.cs @@ -0,0 +1,6 @@ +namespace Bitvault; + +public interface IVaultStorage +{ + bool Create(string name, VaultKey key); +} \ No newline at end of file diff --git a/Bitvault/KeyGenerator.cs b/Bitvault/KeyGenerator.cs new file mode 100644 index 0000000..3ee74d9 --- /dev/null +++ b/Bitvault/KeyGenerator.cs @@ -0,0 +1,14 @@ +using System.Security.Cryptography; +namespace Bitvault; + +public class KeyGenerator : + IKeyGenerator +{ + public byte[] Generate(int size) + { + byte[] key = new byte[size]; + RandomNumberGenerator.Fill(key); + + return key; + } +} diff --git a/Bitvault/OpenVaultHandler.cs b/Bitvault/OpenVaultHandler.cs index 5ad2cc3..d975b70 100644 --- a/Bitvault/OpenVaultHandler.cs +++ b/Bitvault/OpenVaultHandler.cs @@ -12,10 +12,10 @@ public class OpenVaultHandler(IMediator mediator) : { if (vault.Password is { Length: > 0 } password) { - if (await mediator.Handle, bool>(Open.As(new VaultStorage("Personal", password)), cancellationToken)) - { - return true; - } + //if (await mediator.Handle, bool>(Open.As(new VaultStorage("Personal", password)), cancellationToken)) + //{ + // return true; + //} } } diff --git a/Bitvault/OpenVaultStorageHandler.cs b/Bitvault/OpenVaultStorageHandler.cs index 0d74fea..1ae70c2 100644 --- a/Bitvault/OpenVaultStorageHandler.cs +++ b/Bitvault/OpenVaultStorageHandler.cs @@ -1,41 +1,41 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Hosting; -using Toolkit.Foundation; +//using Microsoft.EntityFrameworkCore; +//using Microsoft.Extensions.Hosting; +//using Toolkit.Foundation; -namespace Bitvault; +//namespace Bitvault; -public class OpenVaultStorageHandler(IHostEnvironment environment, - IDbContextFactory dbContextFactory) : IHandler, bool> -{ - public async Task Handle(Open args, CancellationToken cancellationToken) - { - if (args.Value is VaultStorage vault) - { - if (vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password) - { - using VaultDbContext context = dbContextFactory.CreateDbContext(); - var d = context.Database.GetDbConnection().ConnectionString; - context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}.vault;Mode=ReadWriteCreate;Password={password}"); +//public class OpenVaultStorageHandler(IHostEnvironment environment, +// IDbContextFactory dbContextFactory) : IHandler, bool> +//{ +// public async Task Handle(Open args, CancellationToken cancellationToken) +// { +// if (args.Value is VaultStorage vault) +// { +// if (vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password) +// { +// using VaultDbContext context = dbContextFactory.CreateDbContext(); +// var d = context.Database.GetDbConnection().ConnectionString; +// context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}.vault;Mode=ReadWriteCreate;Password={password}"); - bool isOpen = false; - await Task.Run(async () => - { - try - { - await context.Database.OpenConnectionAsync(); - isOpen = true; - } - catch - { - // We are ignoring this exception as it is either a go, or not. - } +// bool isOpen = false; +// await Task.Run(async () => +// { +// try +// { +// await context.Database.OpenConnectionAsync(); +// isOpen = true; +// } +// catch +// { +// // We are ignoring this exception as it is either a go, or not. +// } - }, cancellationToken); +// }, cancellationToken); - return isOpen; - } - } +// return isOpen; +// } +// } - return false; - } -} +// return false; +// } +//} diff --git a/Bitvault/VaultComponentFactory.cs b/Bitvault/VaultComponentFactory.cs new file mode 100644 index 0000000..494c9b4 --- /dev/null +++ b/Bitvault/VaultComponentFactory.cs @@ -0,0 +1,18 @@ +using Toolkit.Foundation; + +namespace Bitvault; + +public class VaultComponentFactory(IComponentFactory componentFactory) : + IVaultComponentFactory +{ + public IComponentHost? Create(string name) + { + if (componentFactory.Create($"Vault:{name}", + new VaultConfiguration { Name = name }) is IComponentHost host) + { + return host; + } + + return default; + } +} diff --git a/Bitvault/VaultConfiguration.cs b/Bitvault/VaultConfiguration.cs index 03149b0..7d0d719 100644 --- a/Bitvault/VaultConfiguration.cs +++ b/Bitvault/VaultConfiguration.cs @@ -5,4 +5,6 @@ namespace Bitvault; public record VaultConfiguration : ComponentConfiguration { public string? Name { get; set; } + + public string? Key { get; set; } } \ No newline at end of file diff --git a/Bitvault/VaultFactory.cs b/Bitvault/VaultFactory.cs new file mode 100644 index 0000000..28576e2 --- /dev/null +++ b/Bitvault/VaultFactory.cs @@ -0,0 +1,13 @@ +namespace Bitvault; + +public class VaultFactory(IVaultKeyGenerator keyGenerator, + IVaultStorage vaultStorageFactory) : + IVaultFactory +{ + public bool Create(string name, + string password) + { + VaultKey key = keyGenerator.Create(password); + return vaultStorageFactory.Create(name, key); + } +} diff --git a/Bitvault/VaultKey.cs b/Bitvault/VaultKey.cs new file mode 100644 index 0000000..2ef97c9 --- /dev/null +++ b/Bitvault/VaultKey.cs @@ -0,0 +1,3 @@ +namespace Bitvault; + +public record VaultKey(byte[] Salt, byte[] Public, byte[] Private); diff --git a/Bitvault/VaultKeyGenerator.cs b/Bitvault/VaultKeyGenerator.cs new file mode 100644 index 0000000..95f47b7 --- /dev/null +++ b/Bitvault/VaultKeyGenerator.cs @@ -0,0 +1,21 @@ +namespace Bitvault; + +public class VaultKeyGenerator(IKeyGenerator generator, + IKeyDeriver deriver, + IEncryptor encryptor, + IDecryptor decryptor) : + IVaultKeyGenerator +{ + public VaultKey Create(string password) + { + byte[] salt = generator.Generate(16); + byte[] key = generator.Generate(32); + + byte[] derivedKey = deriver.DeriveKey(password, salt); + + byte[] publicKey = encryptor.Encrypt(key, derivedKey); + byte[] privateKey = decryptor.Decrypt(publicKey, derivedKey); + + return new VaultKey(salt, publicKey, privateKey); + } +} diff --git a/Bitvault/VaultStorage.cs b/Bitvault/VaultStorage.cs index 8e3f523..d499ec6 100644 --- a/Bitvault/VaultStorage.cs +++ b/Bitvault/VaultStorage.cs @@ -1,3 +1,18 @@ -namespace Bitvault; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Hosting; -public record VaultStorage(string Name, string Password); +namespace Bitvault; + +public class VaultStorage(IHostEnvironment environment, + IDbContextFactory dbContextFactory) : + IVaultStorage +{ + public bool Create(string name, VaultKey key) + { + using VaultDbContext context = dbContextFactory.CreateDbContext(); + context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}" + + $".vault;Mode=ReadWriteCreate;Password={Convert.ToBase64String(key.Private)}"); + + return true; + } +} diff --git a/Toolkit b/Toolkit index 7e980db..b89f21b 160000 --- a/Toolkit +++ b/Toolkit @@ -1 +1 @@ -Subproject commit 7e980dbfce5188631cfa1b8076f42db8f355e85c +Subproject commit b89f21b3ca491b24f826c22fa1e540572f6ffaa9