This commit is contained in:
TheXamlGuy
2024-04-30 20:46:47 +01:00
parent 2a4194ee22
commit 81f266d8c4
22 changed files with 296 additions and 162 deletions
+8 -6
View File
@@ -54,12 +54,17 @@ public partial class App : Application
{ {
args.AddServices(services => args.AddServices(services =>
{ {
services.AddTransient<IKeyGenerator, KeyGenerator>();
services.AddTransient<IEncryptor, AesEncryptor>(); services.AddTransient<IEncryptor, AesEncryptor>();
services.AddTransient<IDecryptor, AesDecryptor>(); services.AddTransient<IDecryptor, AesDecryptor>();
services.AddTransient<IPasswordHasher, PasswordHasher>(); services.AddTransient<IPasswordHasher, PasswordHasher>();
services.AddTransient<IKeyDeriver, KeyDeriver>(); services.AddTransient<IKeyDeriver, KeyDeriver>();
services.AddTransient<IVaultFactory, VaultFactory>();
services.AddTransient<IVaultKeyGenerator, VaultKeyGenerator>();
services.AddTransient<IVaultStorage, VaultStorage>();
services.AddDbContextFactory<VaultDbContext>(args => services.AddDbContextFactory<VaultDbContext>(args =>
{ {
args.UseSqlite(); args.UseSqlite();
@@ -69,9 +74,6 @@ public partial class App : Application
services.AddHandler<OpenVaultHandler>(); services.AddHandler<OpenVaultHandler>();
services.AddHandler<CreateVaultStorageHandler>();
services.AddHandler<OpenVaultStorageHandler>();
services.AddTemplate<VaultNavigationViewModel, VaultNavigationView>(); services.AddTemplate<VaultNavigationViewModel, VaultNavigationView>();
services.AddTemplate<AllNavigationViewModel, AllNavigationView>(); services.AddTemplate<AllNavigationViewModel, AllNavigationView>();
services.AddTemplate<StarredNavigationViewModel, StarredNavigationView>(); services.AddTemplate<StarredNavigationViewModel, StarredNavigationView>();
@@ -83,13 +85,13 @@ public partial class App : Application
}); });
})!); })!);
services.AddSingleton<IVaultHostCollection, VaultHostCollection>(); services.AddTransient<IVaultComponentFactory, VaultComponentFactory>();
services.AddHandler<CreateVaultHandler>(); services.AddHandler<CreateVaultHandler>();
services.AddSingleton<IVaultHostCollection, VaultHostCollection>();
services.AddInitializer<VaultCollectionInitializer>(); services.AddInitializer<VaultCollectionInitializer>();
services.AddTemplate<MainViewModel, MainView>("Main"); services.AddTemplate<MainViewModel, MainView>("Main");
services.AddHandler<VaultNavigationViewModelHandler>(); services.AddHandler<VaultNavigationViewModelHandler>();
services.AddTransient<FooterViewModel>(); services.AddTransient<FooterViewModel>();
+13 -14
View File
@@ -7,23 +7,22 @@ public class AesDecryptor :
{ {
private const int IvSize = 16; 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<byte> iv = cipher.AsSpan(0, IvSize);
ReadOnlySpan<byte> encryptedContent = cipher.AsSpan(IvSize);
byte[] iv = new byte[IvSize]; using Aes aes = Aes.Create();
Array.Copy(cipherData, 0, iv, 0, IvSize); // Extract the IV from the start of the cipher data
using var aes = Aes.Create();
aes.Key = key; aes.Key = key;
aes.IV = iv; aes.IV = iv.ToArray();
using var memoryStream = new MemoryStream(cipherData, IvSize, cipherData.Length - IvSize); using MemoryStream memoryStream = new(encryptedContent.ToArray());
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV)) using ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) using CryptoStream cryptoStream = new(memoryStream, decryptor, CryptoStreamMode.Read);
using (var streamReader = new StreamReader(cryptoStream))
{ using MemoryStream resultStream = new();
return streamReader.ReadToEnd(); // Return the decrypted text cryptoStream.CopyTo(resultStream);
}
return resultStream.ToArray();
} }
} }
+19 -14
View File
@@ -2,28 +2,33 @@
namespace Bitvault; namespace Bitvault;
public class AesEncryptor : IEncryptor public class AesEncryptor :
IEncryptor
{ {
public string Encrypt(string plainText, byte[] key) private const int IvSize = 16;
{
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.Key = key;
aes.GenerateIV(); aes.GenerateIV();
byte[] iv = aes.IV; using MemoryStream memoryStream = new();
memoryStream.Write(aes.IV.AsSpan(0, IvSize));
using var memoryStream = new MemoryStream(); using (ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
memoryStream.Write(iv, 0, IvSize); // Store IV at the start of the stream using (CryptoStream cryptoStream = new(memoryStream, encryptor, CryptoStreamMode.Write))
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
using (var streamWriter = new StreamWriter(cryptoStream))
{ {
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();
} }
} }
+9 -16
View File
@@ -3,30 +3,23 @@ using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
public class CreateVaultHandler(IComponentFactory componentFactory) : public class CreateVaultHandler(IVaultComponentFactory vaultComponentFactory) :
IHandler<Create<Vault>, bool> IHandler<Create<Vault>, bool>
{ {
public async Task<bool> Handle(Create<Vault> args, public Task<bool> Handle(Create<Vault> args,
CancellationToken cancellationToken) 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<IVaultComponent, VaultConfiguration>($"Vault:{name}", new VaultConfiguration { Name = name }) is IComponentHost host) IVaultFactory factory = host.Services.GetRequiredService<IVaultFactory>();
{ factory.Create(name, password);
if (host.Services.GetRequiredService<IMediator>() is IMediator mediator)
{ return Task.FromResult(true);
if (await mediator.Handle<Create<VaultStorage>, bool>(Create.As(new VaultStorage(name, password)), cancellationToken))
{
await host.StartAsync(cancellationToken);
return true;
}
}
}
} }
} }
return false; return Task.FromResult(false);
} }
} }
+75 -59
View File
@@ -1,73 +1,89 @@
using Microsoft.EntityFrameworkCore; //using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting; //using Microsoft.Extensions.Hosting;
using System.Security.Cryptography; //using Toolkit.Foundation;
using Toolkit.Foundation;
namespace Bitvault; //namespace Bitvault;
public class CreateVaultStorageHandler(IHostEnvironment environment, //public class CreateVaultStorageHandler(IHostEnvironment environment,
IKeyDeriver deriver, // IKeyGenerator generator,
IEncryptor encryptor, // IKeyDeriver deriver,
IDecryptor decryptor, // IEncryptor encryptor,
IDbContextFactory<VaultDbContext> dbContextFactory) : IHandler<Create<VaultStorage>, bool> // IDecryptor decryptor,
{ // IDbContextFactory<VaultDbContext> dbContextFactory,
public async Task<bool> Handle(Create<VaultStorage> args, CancellationToken cancellationToken) // IWritableConfiguration<VaultConfiguration> writer) : IHandler<Create<VaultStorage>, bool>
{ //{
if (args.Value is VaultStorage vault) // public async Task<bool> Handle(Create<VaultStorage> args, CancellationToken cancellationToken)
{ // {
if (vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password) // if (args.Value is VaultStorage vault)
{ // {
byte[] salt = new byte[16]; // if (vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password)
RandomNumberGenerator.Fill(salt); // {
// byte[] salt = generator.Generate(16);
// byte[] key = generator.Generate(32);
byte[] key = new byte[32]; // byte[] derivedKey = deriver.DeriveKey(password, salt);
RandomNumberGenerator.Fill(key);
byte[] derivedKey = deriver.DeriveKey(password, salt); // byte[] encryptedKey = encryptor.Encrypt(key, derivedKey);
string? encryptedKey = encryptor.Encrypt(Convert.ToBase64String(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); // //byte[] derivedKey2 = deriver.DeriveKey(password, salt);
var dod = decryptor.Decrypt(encryptedKey, derivedKey2);
// 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 // //string[] parts = hash.Split(':');
//byte[] decryptionKey = deriver.DeriveKey(password, salt);
//// Compare keys to ensure they're the same // //// Store the salt only
//bool areKeysEqual = encryptionKey.SequenceEqual(decryptionKey); // //string storedSalt = parts[0];
////byte[] derivedKey = deriver.DeriveKey(password, salt); // //// Use the hash as the password
//string? encrypted = encryptor.Encrypt(password, derivedKey); // //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); // return false;
// }
//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;
}
}
+5 -5
View File
@@ -1,7 +1,7 @@
namespace Bitvault namespace Bitvault;
public interface IDecryptor
{ {
public interface IDecryptor byte[] Decrypt(byte[] cipher,
{ byte[] key);
string? Decrypt(string cipherText, byte[] key);
}
} }
+5 -6
View File
@@ -1,8 +1,7 @@
namespace Bitvault namespace Bitvault;
{
public interface IEncryptor
{
string? Encrypt(string plainText, byte[] key); public interface IEncryptor
} {
byte[] Encrypt(byte[] data, byte[] key);
} }
+6
View File
@@ -0,0 +1,6 @@
namespace Bitvault;
public interface IKeyGenerator
{
byte[] Generate(int size);
}
+9
View File
@@ -0,0 +1,9 @@
using Toolkit.Foundation;
namespace Bitvault
{
public interface IVaultComponentFactory
{
IComponentHost? Create(string name);
}
}
+7
View File
@@ -0,0 +1,7 @@
namespace Bitvault
{
public interface IVaultFactory
{
bool Create(string name, string password);
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace Bitvault;
public interface IVaultKeyGenerator
{
VaultKey Create(string password);
}
+6
View File
@@ -0,0 +1,6 @@
namespace Bitvault;
public interface IVaultStorage
{
bool Create(string name, VaultKey key);
}
+14
View File
@@ -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;
}
}
+4 -4
View File
@@ -12,10 +12,10 @@ 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)) //if (await mediator.Handle<Open<VaultStorage>, bool>(Open.As(new VaultStorage("Personal", password)), cancellationToken))
{ //{
return true; // return true;
} //}
} }
} }
+35 -35
View File
@@ -1,41 +1,41 @@
using Microsoft.EntityFrameworkCore; //using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting; //using Microsoft.Extensions.Hosting;
using Toolkit.Foundation; //using Toolkit.Foundation;
namespace Bitvault; //namespace Bitvault;
public class OpenVaultStorageHandler(IHostEnvironment environment, //public class OpenVaultStorageHandler(IHostEnvironment environment,
IDbContextFactory<VaultDbContext> dbContextFactory) : IHandler<Open<VaultStorage>, bool> // IDbContextFactory<VaultDbContext> dbContextFactory) : IHandler<Open<VaultStorage>, bool>
{ //{
public async Task<bool> Handle(Open<VaultStorage> args, CancellationToken cancellationToken) // public async Task<bool> Handle(Open<VaultStorage> args, CancellationToken cancellationToken)
{ // {
if (args.Value is VaultStorage vault) // if (args.Value is VaultStorage vault)
{ // {
if (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)
{ // {
using VaultDbContext context = dbContextFactory.CreateDbContext(); // using VaultDbContext context = dbContextFactory.CreateDbContext();
var d = context.Database.GetDbConnection().ConnectionString; // var d = context.Database.GetDbConnection().ConnectionString;
context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}.vault;Mode=ReadWriteCreate;Password={password}"); // context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}.vault;Mode=ReadWriteCreate;Password={password}");
bool isOpen = false; // bool isOpen = false;
await Task.Run(async () => // await Task.Run(async () =>
{ // {
try // try
{ // {
await context.Database.OpenConnectionAsync(); // await context.Database.OpenConnectionAsync();
isOpen = true; // isOpen = true;
} // }
catch // catch
{ // {
// We are ignoring this exception as it is either a go, or not. // // We are ignoring this exception as it is either a go, or not.
} // }
}, cancellationToken); // }, cancellationToken);
return isOpen; // return isOpen;
} // }
} // }
return false; // return false;
} // }
} //}
+18
View File
@@ -0,0 +1,18 @@
using Toolkit.Foundation;
namespace Bitvault;
public class VaultComponentFactory(IComponentFactory componentFactory) :
IVaultComponentFactory
{
public IComponentHost? Create(string name)
{
if (componentFactory.Create<IVaultComponent, VaultConfiguration>($"Vault:{name}",
new VaultConfiguration { Name = name }) is IComponentHost host)
{
return host;
}
return default;
}
}
+2
View File
@@ -5,4 +5,6 @@ namespace Bitvault;
public record VaultConfiguration : ComponentConfiguration public record VaultConfiguration : ComponentConfiguration
{ {
public string? Name { get; set; } public string? Name { get; set; }
public string? Key { get; set; }
} }
+13
View File
@@ -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);
}
}
+3
View File
@@ -0,0 +1,3 @@
namespace Bitvault;
public record VaultKey(byte[] Salt, byte[] Public, byte[] Private);
+21
View File
@@ -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);
}
}
+17 -2
View File
@@ -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<VaultDbContext> 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;
}
}
+1 -1
Submodule Toolkit updated: 7e980dbfce...b89f21b3ca