bunch of fixes
This commit is contained in:
@@ -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" />
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Bitvault;
|
||||
|
||||
public interface IDecryptor
|
||||
{
|
||||
byte[] Decrypt(byte[] cipher,
|
||||
byte[] key);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Bitvault;
|
||||
|
||||
public interface IEncryptor
|
||||
{
|
||||
|
||||
byte[] Encrypt(byte[] data, byte[] key);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Bitvault;
|
||||
|
||||
public interface IKeyDeriver
|
||||
{
|
||||
byte[] DeriveKey(string password, byte[] salt, int keySize = 32, int iterations = 10000);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Bitvault;
|
||||
|
||||
public interface IKeyGenerator
|
||||
{
|
||||
byte[] Generate(int size);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Bitvault;
|
||||
|
||||
public interface IPasswordHasher
|
||||
{
|
||||
string HashPassword(string password, int iterations = 10000);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Bitvault
|
||||
{
|
||||
public interface IVaultFactory
|
||||
{
|
||||
bool Create(string name, string password);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -2,5 +2,5 @@
|
||||
|
||||
public interface IVaultStorage
|
||||
{
|
||||
bool Create(string name, VaultKey key);
|
||||
Task<bool> CreateAsync(string name, VaultKey key);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)}";
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user