bunch of fixes

This commit is contained in:
TheXamlGuy
2024-05-02 20:58:12 +01:00
parent e98e622003
commit 4b05abad9b
23 changed files with 115 additions and 192 deletions
@@ -5,6 +5,8 @@
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia.Desktop" Version="11.1.0-beta1" />
+13 -6
View File
@@ -4,8 +4,11 @@ using Avalonia.Markup.Xaml;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using Toolkit.Avalonia;
using Toolkit.Foundation;
using System.IO;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Bitvault.Avalonia;
@@ -61,17 +64,21 @@ public partial class App : Application
services.AddTransient<IPasswordHasher, PasswordHasher>();
services.AddTransient<IKeyDeriver, KeyDeriver>();
services.AddTransient<IVaultFactory, VaultFactory>();
services.AddTransient<IVaultKeyGenerator, VaultKeyGenerator>();
services.AddTransient<IVaultKeyFactory, VaultKeyFactory>();
services.AddTransient<IVaultInitializer, VaultInitializer>();
services.AddTransient<IVaultStorage, VaultStorage>();
services.TryAddSingleton<IContainer<VaultKey>, Container<VaultKey>>();
services.TryAddSingleton<IContainer<VaultStorageConnection>, Container<VaultStorageConnection>>();
services.AddDbContextFactory<VaultDbContext>(args =>
services.AddDbContextFactory<VaultDbContext>((provider, args) =>
{
args.UseSqlite();
if (provider.GetRequiredService<IContainer<VaultStorageConnection>>()
is IContainer<VaultStorageConnection> connection)
{
args.UseSqlite($"{connection.Value}");
}
});
services.AddDbContextFactory<VaultDbContext>();
services.AddHandler<OpenVaultHandler>();
services.AddTemplate<VaultNavigationViewModel, VaultNavigationView>();
-28
View File
@@ -1,28 +0,0 @@
using System.Security.Cryptography;
namespace Bitvault;
public class AesDecryptor :
IDecryptor
{
private const int IvSize = 16;
public byte[] Decrypt(byte[] cipher, byte[] key)
{
Span<byte> iv = cipher.AsSpan(0, IvSize);
ReadOnlySpan<byte> encryptedContent = cipher.AsSpan(IvSize);
using Aes aes = Aes.Create();
aes.Key = key;
aes.IV = iv.ToArray();
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();
}
}
-34
View File
@@ -1,34 +0,0 @@
using System.Security.Cryptography;
namespace Bitvault;
public class AesEncryptor :
IEncryptor
{
private const int IvSize = 16;
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();
using MemoryStream memoryStream = new();
memoryStream.Write(aes.IV.AsSpan(0, IvSize));
using (ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
using (CryptoStream cryptoStream = new(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
}
return memoryStream.ToArray();
}
}
+12 -9
View File
@@ -1,25 +1,28 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Toolkit.Foundation;
namespace Bitvault;
public class CreateVaultHandler(IVaultComponentFactory vaultComponentFactory) :
public class CreateVaultHandler(IVaultComponentFactory componentFactory) :
IHandler<Create<Vault>, bool>
{
public Task<bool> Handle(Create<Vault> args,
public async Task<bool> Handle(Create<Vault> args,
CancellationToken cancellationToken)
{
if (args.Value is Vault vault && vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password)
if (args.Value is Vault vault && vault.Name is { Length: > 0 } name &&
vault.Password is { Length: > 0 } password)
{
if (vaultComponentFactory.Create(name) is IComponentHost host)
if (componentFactory.Create(name) is IComponentHost host)
{
IVaultFactory factory = host.Services.GetRequiredService<IVaultFactory>();
factory.Create(name, password);
return Task.FromResult(true);
IVaultInitializer initializer = host.Services.GetRequiredService<IVaultInitializer>();
if (await initializer.Initialize(name, password))
{
host.Start();
}
}
}
return Task.FromResult(false);
return false;
}
}
-7
View File
@@ -1,7 +0,0 @@
namespace Bitvault;
public interface IDecryptor
{
byte[] Decrypt(byte[] cipher,
byte[] key);
}
-7
View File
@@ -1,7 +0,0 @@
namespace Bitvault;
public interface IEncryptor
{
byte[] Encrypt(byte[] data, byte[] key);
}
-6
View File
@@ -1,6 +0,0 @@
namespace Bitvault;
public interface IKeyDeriver
{
byte[] DeriveKey(string password, byte[] salt, int keySize = 32, int iterations = 10000);
}
-6
View File
@@ -1,6 +0,0 @@
namespace Bitvault;
public interface IKeyGenerator
{
byte[] Generate(int size);
}
-6
View File
@@ -1,6 +0,0 @@
namespace Bitvault;
public interface IPasswordHasher
{
string HashPassword(string password, int iterations = 10000);
}
-7
View File
@@ -1,7 +0,0 @@
namespace Bitvault
{
public interface IVaultFactory
{
bool Create(string name, string password);
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace Bitvault;
public interface IVaultInitializer
{
Task<bool> Initialize(string name, string password);
}
@@ -1,6 +1,6 @@
namespace Bitvault;
public interface IVaultKeyGenerator
public interface IVaultKeyFactory
{
VaultKey Create(string password);
}
}
+1 -1
View File
@@ -2,5 +2,5 @@
public interface IVaultStorage
{
bool Create(string name, VaultKey key);
Task<bool> CreateAsync(string name, VaultKey key);
}
-13
View File
@@ -1,13 +0,0 @@
using System.Security.Cryptography;
namespace Bitvault;
public class KeyDeriver :
IKeyDeriver
{
public byte[] DeriveKey(string password, byte[] salt, int keySize = 32, int iterations = 100000)
{
using Rfc2898DeriveBytes pbkdf2 = new(password, salt, iterations, HashAlgorithmName.SHA256);
return pbkdf2.GetBytes(keySize);
}
}
-14
View File
@@ -1,14 +0,0 @@
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;
}
}
+1 -4
View File
@@ -12,10 +12,7 @@ public class OpenVaultHandler(IMediator mediator) :
{
if (vault.Password is { Length: > 0 } password)
{
//if (await mediator.Handle<Open<VaultStorage>, bool>(Open.As(new VaultStorage("Personal", password)), cancellationToken))
//{
// return true;
//}
}
}
-19
View File
@@ -1,19 +0,0 @@
using System.Security.Cryptography;
namespace Bitvault;
public class PasswordHasher :
IPasswordHasher
{
private const int SaltSize = 16;
public string HashPassword(string password, int iterations = 10000)
{
using Rfc2898DeriveBytes pbkdf2 = new(password, SaltSize, iterations, HashAlgorithmName.SHA256);
byte[] salt = pbkdf2.Salt;
byte[] hash = pbkdf2.GetBytes(32);
return $"{Convert.ToBase64String(salt)}:{Convert.ToBase64String(hash)}";
}
}
-13
View File
@@ -1,13 +0,0 @@
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);
}
}
+32
View File
@@ -0,0 +1,32 @@
using Toolkit.Foundation;
namespace Bitvault;
public class VaultStorageConnection(string connection)
{
private readonly string connection = connection;
public override string ToString() => connection;
}
public class VaultInitializer(IVaultKeyFactory keyVaultFactory,
IContainer<VaultKey> vaultKeyContainer,
IVaultStorage vaultStorage,
IWritableConfiguration<VaultConfiguration> configuration) :
IVaultInitializer
{
public async Task<bool> Initialize(string name,
string password)
{
VaultKey key = keyVaultFactory.Create(password);
vaultKeyContainer.Set(key);
if (await vaultStorage.CreateAsync(name, key))
{
configuration.Write(args => args.Key = $"{Convert.ToBase64String(key.Phrase)}:{Convert.ToBase64String(key.Public)}");
return true;
}
return false;
}
}
+12 -1
View File
@@ -1,3 +1,14 @@
namespace Bitvault;
public record VaultKey(byte[] Salt, byte[] Public, byte[] Private);
public record VaultKey(byte[] Phrase, byte[] Public, byte[] Private) :
IDisposable
{
public void Dispose()
{
GC.SuppressFinalize(this);
Array.Clear(Phrase, 0, Phrase.Length);
Array.Clear(Public, 0, Public.Length);
Array.Clear(Private, 0, Private.Length);
}
}
@@ -1,10 +1,12 @@
namespace Bitvault;
using Toolkit.Foundation;
public class VaultKeyGenerator(IKeyGenerator generator,
namespace Bitvault;
public class VaultKeyFactory(IKeyGenerator generator,
IKeyDeriver deriver,
IEncryptor encryptor,
IDecryptor decryptor) :
IVaultKeyGenerator
IVaultKeyFactory
{
public VaultKey Create(string password)
{
@@ -19,3 +21,4 @@ public class VaultKeyGenerator(IKeyGenerator generator,
return new VaultKey(salt, publicKey, privateKey);
}
}
+28 -6
View File
@@ -1,17 +1,39 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Toolkit.Foundation;
namespace Bitvault;
public class VaultStorage(IHostEnvironment environment,
IDbContextFactory<VaultDbContext> dbContextFactory) :
public class VaultStorage(IContainer<VaultStorageConnection> connection,
IHostEnvironment environment,
IServiceProvider provider) :
IVaultStorage
{
public bool Create(string name, VaultKey key)
public async Task<bool> CreateAsync(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)}");
connection.Set(new VaultStorageConnection($"Data Source={Path.Combine(environment.ContentRootPath, name)}" +
$".vault;Mode=ReadWriteCreate;Pooling=false;Password={Convert.ToBase64String(key.Private)}"));
IDbContextFactory<VaultDbContext> dbContextFactory = provider.GetRequiredService<IDbContextFactory<VaultDbContext>>();
using VaultDbContext context = await dbContextFactory.CreateDbContextAsync();
try
{
await Task.Run(async () =>
{
await context.Database.EnsureCreatedAsync().ConfigureAwait(false);
await context.Database.CloseConnectionAsync().ConfigureAwait(false);
context.Database.SetConnectionString(null);
}).ConfigureAwait(false);
}
catch
{
return false;
}
return true;
}