bunch of fixes
This commit is contained in:
@@ -5,6 +5,8 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
<SelfContained>true</SelfContained>
|
||||||
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia.Desktop" Version="11.1.0-beta1" />
|
<PackageReference Include="Avalonia.Desktop" Version="11.1.0-beta1" />
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ using Avalonia.Markup.Xaml;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using System;
|
||||||
using Toolkit.Avalonia;
|
using Toolkit.Avalonia;
|
||||||
using Toolkit.Foundation;
|
using Toolkit.Foundation;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|
||||||
namespace Bitvault.Avalonia;
|
namespace Bitvault.Avalonia;
|
||||||
|
|
||||||
@@ -61,17 +64,21 @@ public partial class App : Application
|
|||||||
services.AddTransient<IPasswordHasher, PasswordHasher>();
|
services.AddTransient<IPasswordHasher, PasswordHasher>();
|
||||||
services.AddTransient<IKeyDeriver, KeyDeriver>();
|
services.AddTransient<IKeyDeriver, KeyDeriver>();
|
||||||
|
|
||||||
services.AddTransient<IVaultFactory, VaultFactory>();
|
services.AddTransient<IVaultKeyFactory, VaultKeyFactory>();
|
||||||
services.AddTransient<IVaultKeyGenerator, VaultKeyGenerator>();
|
services.AddTransient<IVaultInitializer, VaultInitializer>();
|
||||||
services.AddTransient<IVaultStorage, VaultStorage>();
|
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.AddHandler<OpenVaultHandler>();
|
||||||
|
|
||||||
services.AddTemplate<VaultNavigationViewModel, VaultNavigationView>();
|
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.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
using Toolkit.Foundation;
|
using Toolkit.Foundation;
|
||||||
|
|
||||||
namespace Bitvault;
|
namespace Bitvault;
|
||||||
|
|
||||||
public class CreateVaultHandler(IVaultComponentFactory vaultComponentFactory) :
|
public class CreateVaultHandler(IVaultComponentFactory componentFactory) :
|
||||||
IHandler<Create<Vault>, bool>
|
IHandler<Create<Vault>, bool>
|
||||||
{
|
{
|
||||||
public Task<bool> Handle(Create<Vault> args,
|
public async Task<bool> Handle(Create<Vault> args,
|
||||||
CancellationToken cancellationToken)
|
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>();
|
IVaultInitializer initializer = host.Services.GetRequiredService<IVaultInitializer>();
|
||||||
factory.Create(name, password);
|
if (await initializer.Initialize(name, password))
|
||||||
|
{
|
||||||
return Task.FromResult(true);
|
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;
|
namespace Bitvault;
|
||||||
|
|
||||||
public interface IVaultKeyGenerator
|
public interface IVaultKeyFactory
|
||||||
{
|
{
|
||||||
VaultKey Create(string password);
|
VaultKey Create(string password);
|
||||||
}
|
}
|
||||||
@@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
public interface IVaultStorage
|
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 (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;
|
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,
|
IKeyDeriver deriver,
|
||||||
IEncryptor encryptor,
|
IEncryptor encryptor,
|
||||||
IDecryptor decryptor) :
|
IDecryptor decryptor) :
|
||||||
IVaultKeyGenerator
|
IVaultKeyFactory
|
||||||
{
|
{
|
||||||
public VaultKey Create(string password)
|
public VaultKey Create(string password)
|
||||||
{
|
{
|
||||||
@@ -19,3 +21,4 @@ public class VaultKeyGenerator(IKeyGenerator generator,
|
|||||||
return new VaultKey(salt, publicKey, privateKey);
|
return new VaultKey(salt, publicKey, privateKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,17 +1,39 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Toolkit.Foundation;
|
||||||
|
|
||||||
namespace Bitvault;
|
namespace Bitvault;
|
||||||
|
|
||||||
public class VaultStorage(IHostEnvironment environment,
|
public class VaultStorage(IContainer<VaultStorageConnection> connection,
|
||||||
IDbContextFactory<VaultDbContext> dbContextFactory) :
|
IHostEnvironment environment,
|
||||||
|
IServiceProvider provider) :
|
||||||
IVaultStorage
|
IVaultStorage
|
||||||
{
|
{
|
||||||
public bool Create(string name, VaultKey key)
|
public async Task<bool> CreateAsync(string name,
|
||||||
|
VaultKey key)
|
||||||
{
|
{
|
||||||
using VaultDbContext context = dbContextFactory.CreateDbContext();
|
connection.Set(new VaultStorageConnection($"Data Source={Path.Combine(environment.ContentRootPath, name)}" +
|
||||||
context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}" +
|
$".vault;Mode=ReadWriteCreate;Pooling=false;Password={Convert.ToBase64String(key.Private)}"));
|
||||||
$".vault;Mode=ReadWriteCreate;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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user